html5 localStorage ошибка с Safari: "QUOTA_EXCEEDED_ERR: DOM Exception 22: была предпринята попытка добавить что-то в хранилище, которое превысило квоту".

В моем веб-приложении есть ошибки javascript в приватном просмотре ios safari:

JavaScript: ошибка

не определено

QUOTA_EXCEEDED_ERR:DOM Exception 22: была сделана попытка добавить что-то в хранилище...

мой код:

localStorage.setItem('test',1)

12 ответов

Решение

Видимо это по замыслу. Когда Safari (OS X или iOS) находится в режиме частного просмотра, он выглядит как localStorage доступен, но пытается позвонить setItem бросает исключение.

store.js line 73
"QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."

Что происходит, так это то, что объект window по-прежнему localStorage в глобальном пространстве имен, но когда вы звоните setItemэто исключение выбрасывается. Любые звонки removeItem игнорируются

Я считаю, что самое простое решение (хотя я еще не тестировал этот кросс-браузер) было бы изменить функцию isLocalStorageNameSupported() чтобы проверить, что вы также можете установить некоторое значение.

https://github.com/marcuswestin/store.js/issues/42

function isLocalStorageNameSupported() 
{
    var testKey = 'test', storage = window.sessionStorage;
    try 
    {
        storage.setItem(testKey, '1');
        storage.removeItem(testKey);
        return localStorageName in win && win[localStorageName];
    } 
    catch (error) 
    {
        return false;
    }
}

Исправление, размещенное по вышеуказанной ссылке, не работает для меня. Это сделал:

function isLocalStorageNameSupported() {
  var testKey = 'test', storage = window.localStorage;
  try {
    storage.setItem(testKey, '1');
    storage.removeItem(testKey);
    return true;
  } catch (error) {
    return false;
  }
}

Получено из http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5

Как упоминалось в других ответах, вы всегда будете получать ошибку QuotaExceededError в режиме приватного браузера Safari на iOS и OS X, когда localStorage.setItem (или же sessionStorage.setItem) называется.

Одним из решений является проверка try / catch или Modernizr в каждом случае использования setItem,

Однако, если вы хотите, чтобы шим, который просто глобально останавливал эту ошибку, чтобы не допустить взлома остальной части вашего JavaScript, вы можете использовать это:

https://gist.github.com/philfreo/68ea3cd980d72383c951

// Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem
// throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem
// to avoid the entire page breaking, without having to do a check at each usage of Storage.
if (typeof localStorage === 'object') {
    try {
        localStorage.setItem('localStorage', 1);
        localStorage.removeItem('localStorage');
    } catch (e) {
        Storage.prototype._setItem = Storage.prototype.setItem;
        Storage.prototype.setItem = function() {};
        alert('Your web browser does not support storing settings locally. In Safari, the most common cause of this is using "Private Browsing Mode". Some settings may not save or some features may not work properly for you.');
    }
}

В моем контексте только что разработали абстракцию класса. Когда мое приложение запускается, я проверяю, работает ли localStorage, вызывая getStorage (). Эта функция также возвращает:

  • либо localStorage, если localStorage работает
  • или реализация пользовательского класса LocalStorageAlternative

В моем коде я никогда не вызываю localStorage напрямую. Я вызываю глобальную переменную cusSto, которую я инициализировал, вызвав getStorage ().

Таким образом, он работает с приватным просмотром или определенными версиями Safari.

function getStorage() {

    var storageImpl;

     try { 
        localStorage.setItem("storage", ""); 
        localStorage.removeItem("storage");
        storageImpl = localStorage;
     }
     catch (err) { 
         storageImpl = new LocalStorageAlternative();
     }

    return storageImpl;

}

function LocalStorageAlternative() {

    var structureLocalStorage = {};

    this.setItem = function (key, value) {
        structureLocalStorage[key] = value;
    }

    this.getItem = function (key) {
        if(typeof structureLocalStorage[key] != 'undefined' ) {
            return structureLocalStorage[key];
        }
        else {
            return null;
        }
    }

    this.removeItem = function (key) {
        structureLocalStorage[key] = undefined;
    }
}

cusSto = getStorage();

Кажется, что Safari 11 меняет поведение, и теперь локальное хранилище работает в частном окне браузера. Ура!

Наше веб-приложение, которое раньше не работало в режиме приватного просмотра Safari, теперь работает безупречно. Он всегда работал нормально в режиме частного просмотра Chrome, который всегда позволял записывать в локальное хранилище.

Это задокументировано в заметках о выпуске Apple Safari Technology Preview - и в заметках о выпуске WebKit - для выпуска 29, выпущенного в мае 2017 года.

В частности:

  • Исправлена ​​ошибка QuotaExceededError при сохранении в localStorage в режиме частного просмотра или сеансах WebDriver - r215315

Чтобы расширить ответы других, вот компактное решение, которое не раскрывает и не добавляет новые переменные. Он не охватывает все основы, но должен подходить большинству людей, которые хотят, чтобы одностраничное приложение оставалось работоспособным (несмотря на отсутствие сохранения данных после перезагрузки).

(function(){
    try {
        localStorage.setItem('_storage_test', 'test');
        localStorage.removeItem('_storage_test');
    } catch (exc){
        var tmp_storage = {};
        var p = '__unique__';  // Prefix all keys to avoid matching built-ins
        Storage.prototype.setItem = function(k, v){
            tmp_storage[p + k] = v;
        };
        Storage.prototype.getItem = function(k){
            return tmp_storage[p + k] === undefined ? null : tmp_storage[p + k];
        };
        Storage.prototype.removeItem = function(k){
            delete tmp_storage[p + k];
        };
        Storage.prototype.clear = function(){
            tmp_storage = {};
        };
    }
})();

У меня была такая же проблема с использованием Ionic Framework (Angular + Cordova). Я знаю, что это не решает проблему, но это код для Angular Apps, основанный на ответах выше. У вас будет временное решение для localStorage в iOS-версии Safari.

Вот код:

angular.module('myApp.factories', [])
.factory('$fakeStorage', [
    function(){
        function FakeStorage() {};
        FakeStorage.prototype.setItem = function (key, value) {
            this[key] = value;
        };
        FakeStorage.prototype.getItem = function (key) {
            return typeof this[key] == 'undefined' ? null : this[key];
        }
        FakeStorage.prototype.removeItem = function (key) {
            this[key] = undefined;
        };
        FakeStorage.prototype.clear = function(){
            for (var key in this) {
                if( this.hasOwnProperty(key) )
                {
                    this.removeItem(key);
                }
            }
        };
        FakeStorage.prototype.key = function(index){
            return Object.keys(this)[index];
        };
        return new FakeStorage();
    }
])
.factory('$localstorage', [
    '$window', '$fakeStorage',
    function($window, $fakeStorage) {
        function isStorageSupported(storageName) 
        {
            var testKey = 'test',
                storage = $window[storageName];
            try
            {
                storage.setItem(testKey, '1');
                storage.removeItem(testKey);
                return true;
            } 
            catch (error) 
            {
                return false;
            }
        }
        var storage = isStorageSupported('localStorage') ? $window.localStorage : $fakeStorage;
        return {
            set: function(key, value) {
                storage.setItem(key, value);
            },
            get: function(key, defaultValue) {
                return storage.getItem(key) || defaultValue;
            },
            setObject: function(key, value) {
                storage.setItem(key, JSON.stringify(value));
            },
            getObject: function(key) {
                return JSON.parse(storage.getItem(key) || '{}');
            },
            remove: function(key){
                storage.removeItem(key);
            },
            clear: function() {
                storage.clear();
            },
            key: function(index){
                storage.key(index);
            }
        }
    }
]);

Источник: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871

Приятного кодирования!

Принятый ответ в некоторых ситуациях кажется неадекватным.

Чтобы проверить, localStorage или sessionStorageподдерживаются, я использую следующий фрагмент из MDN.

function storageAvailable(type) {
    var storage;
    try {
        storage = window[type];
        var x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
    }
    catch(e) {
        return e instanceof DOMException && (
            // everything except Firefox
            e.code === 22 ||
            // Firefox
            e.code === 1014 ||
            // test name field too, because code might not be present
            // everything except Firefox
            e.name === 'QuotaExceededError' ||
            // Firefox
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
            // acknowledge QuotaExceededError only if there's something already stored
            (storage && storage.length !== 0);
    }
}

Используйте этот фрагмент вот так, а затем, например, используйте cookie:

if (storageAvailable('localStorage')) {
  // Yippee! We can use localStorage awesomeness
}
else {
  // Too bad, no localStorage for us
  document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}

Я сделал пакет fallbackstorage, который использует этот фрагмент для проверки доступности хранилища и отката к вручную реализованному MemoryStorage.

import {getSafeStorage} from 'fallbackstorage'

getSafeStorage().setItem('test', '1') // always work

Вот решение для AngularJS, использующее IIFE и использующее тот факт, что сервисы являются одиночными.

Это приводит к isLocalStorageAvailable устанавливается сразу при первом введении службы и позволяет избежать ненужного запуска проверки каждый раз, когда требуется доступ к локальному хранилищу.

angular.module('app.auth.services', []).service('Session', ['$log', '$window',
  function Session($log, $window) {
    var isLocalStorageAvailable = (function() {
      try {
        $window.localStorage.world = 'hello';
        delete $window.localStorage.world;
        return true;
      } catch (ex) {
        return false;
      }
    })();

    this.store = function(key, value) {
      if (isLocalStorageAvailable) {
        $window.localStorage[key] = value;
      } else {
        $log.warn('Local Storage is not available');
      }
    };
  }
]);

Вот сервисная версия Angular2+ для альтернативы памяти, которую вы можете просто вставить в свои компоненты, основываясь на ответе Пьера Ле Ру.

import { Injectable } from '@angular/core';

// Alternative to localstorage, memory
// storage for certain browsers in private mode
export class LocalStorageAlternative {
    private  structureLocalStorage = {};

    setItem(key: string, value: string): void {
        this.structureLocalStorage[key] = value;
    }

    getItem(key: string): string {
        if (typeof this.structureLocalStorage[key] !== 'undefined' ) {
            return this.structureLocalStorage[key];
        }
        return null;
    }

    removeItem(key: string): void {
        this.structureLocalStorage[key] = undefined;
    }
}

@Injectable()
export class StorageService {
    private storageEngine;

    constructor() {
        try {
            localStorage.setItem('storage_test', '');
            localStorage.removeItem('storage_test');
            this.storageEngine = localStorage;
        } catch (err) {
            this.storageEngine = new LocalStorageAlternative();
        }
    }

    setItem(key: string, value: string): void {
        this.storageEngine.setItem(key, value);
    }

    getItem(key: string): string {
        return this.storageEngine.getItem(key);
    }

    removeItem(key: string): void {
        this.storageEngine.removeItem(key);
    }

}

Я только что создал этот репо, чтобы обеспечить sessionStorage а также localStorage функции для неподдерживаемых или отключенных браузеров.

Поддерживаемые браузеры

  • IE5 +
  • Chrome все версии
  • Mozilla все версии
  • Яндекс все версии

Как это устроено

Он обнаруживает функцию с типом хранилища.

function(type) {
    var testKey = '__isSupported',
        storage = window[type];
    try {
        storage.setItem(testKey, '1');
        storage.removeItem(testKey);
        return true;
    } catch (error) {
        return false;
    }
};

наборы StorageService.localStorage в window.localStorage если это поддерживается или создает хранилище cookie. наборы StorageService.sessionStorage в window.sessionStorage если это поддерживается или создает в памяти хранилище для SPA, хранилище cookie с функциями sesion для не SPA.

Я создал исправление для этой проблемы. Просто я проверяю, поддерживает ли браузер localStorage или sessionStorage или нет. Если нет, то механизм хранения будет Cookie. Но отрицательной стороной является то, что у Cookie очень крошечная память:(

function StorageEngine(engine) {
    this.engine = engine || 'localStorage';

    if(!this.checkStorageApi(this.engine)) {
        // Default engine would be alway cooke
        // Safari private browsing issue with localStorage / sessionStorage
        this.engine = 'cookie';
    }
}

StorageEngine.prototype.checkStorageApi = function(name) {
    if(!window[name]) return false;
    try {
        var tempKey = '__temp_'+Date.now();
        window[name].setItem(tempKey, 'hi')
        window[name].removeItem(tempKey);
        return true;
    } catch(e) {
        return false;
    }
}

StorageEngine.prototype.getItem = function(key) {
    if(['sessionStorage', 'localStorage'].includes(this.engine)) {
        return window[this.engine].getItem(key);
    } else if('cookie') {
        var name = key+"=";
        var allCookie = decodeURIComponent(document.cookie).split(';');
        var cval = [];
        for(var i=0; i < allCookie.length; i++) {
            if (allCookie[i].trim().indexOf(name) == 0) {
                cval = allCookie[i].trim().split("=");
            }   
        }
        return (cval.length > 0) ? cval[1] : null;
    }
    return null;
}

StorageEngine.prototype.setItem = function(key, val, exdays) {
    if(['sessionStorage', 'localStorage'].includes(this.engine)) {
        window[this.engine].setItem(key, val);
    } else if('cookie') {
        var d = new Date();
        var exdays = exdays || 1;
        d.setTime(d.getTime() + (exdays*24*36E5));
        var expires = "expires="+ d.toUTCString();
        document.cookie = key + "=" + val + ";" + expires + ";path=/";
    }
    return true;
}


// ------------------------
var StorageEngine = new StorageEngine(); // new StorageEngine('localStorage');
// If your current browser (IOS safary or any) does not support localStorage/sessionStorage, then the default engine will be "cookie"

StorageEngine.setItem('keyName', 'val')

var expireDay = 1; // for cookie only
StorageEngine.setItem('keyName', 'val', expireDay)
StorageEngine.getItem('keyName')

Не используйте его, если он не поддерживается, а для проверки поддержки просто вызовите эту функцию

совместное использование в Es6 полного чтения и записи Пример localStorage с проверкой поддержки

const LOCAL_STORAGE_KEY = 'tds_app_localdata';

const isSupported = () => {
  try {
    localStorage.setItem('supported', '1');
    localStorage.removeItem('supported');
    return true;
  } catch (error) {
    return false;
  }
};


const writeToLocalStorage =
  components =>
    (isSupported ?
      localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(components))
      : components);

const isEmpty = component => (!component || Object.keys(component).length === 0);

const readFromLocalStorage =
  () => (isSupported ? JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)) || {} : null);

Это обеспечит правильную установку и получение ключей во всех браузерах.

var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }

Следующий скрипт решил мою проблему:

// Fake localStorage implementation. 
// Mimics localStorage, including events. 
// It will work just like localStorage, except for the persistant storage part. 

var fakeLocalStorage = function() {
  var fakeLocalStorage = {};
  var storage; 

  // If Storage exists we modify it to write to our fakeLocalStorage object instead. 
  // If Storage does not exist we create an empty object. 
  if (window.Storage && window.localStorage) {
    storage = window.Storage.prototype; 
  } else {
    // We don't bother implementing a fake Storage object
    window.localStorage = {}; 
    storage = window.localStorage; 
  }

  // For older IE
  if (!window.location.origin) {
    window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
  }

  var dispatchStorageEvent = function(key, newValue) {
    var oldValue = (key == null) ? null : storage.getItem(key); // `==` to match both null and undefined
    var url = location.href.substr(location.origin.length);
    var storageEvent = document.createEvent('StorageEvent'); // For IE, http://stackru.com/a/25514935/1214183

    storageEvent.initStorageEvent('storage', false, false, key, oldValue, newValue, url, null);
    window.dispatchEvent(storageEvent);
  };

  storage.key = function(i) {
    var key = Object.keys(fakeLocalStorage)[i];
    return typeof key === 'string' ? key : null;
  };

  storage.getItem = function(key) {
    return typeof fakeLocalStorage[key] === 'string' ? fakeLocalStorage[key] : null;
  };

  storage.setItem = function(key, value) {
    dispatchStorageEvent(key, value);
    fakeLocalStorage[key] = String(value);
  };

  storage.removeItem = function(key) {
    dispatchStorageEvent(key, null);
    delete fakeLocalStorage[key];
  };

  storage.clear = function() {
    dispatchStorageEvent(null, null);
    fakeLocalStorage = {};
  };
};

// Example of how to use it
if (typeof window.localStorage === 'object') {
  // Safari will throw a fit if we try to use localStorage.setItem in private browsing mode. 
  try {
    localStorage.setItem('localStorageTest', 1);
    localStorage.removeItem('localStorageTest');
  } catch (e) {
    fakeLocalStorage();
  }
} else {
  // Use fake localStorage for any browser that does not support it.
  fakeLocalStorage();
}

Он проверяет, существует ли localStorage и может ли он использоваться, а в отрицательном случае создает поддельное локальное хранилище и использует его вместо исходного localStorage. Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

Другие вопросы по тегам