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

У моего webapp есть ошибки javascript в частном просмотре iOS safari:

JavaScript-кода:ошибка

undefined

QUOTA_EXCEEDED_ERR: DOM исключение 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) называется.

одно решение - сделать попытку / поймать или 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 напрямую. Я зову cusStoglobal var, я инициализировал позвонив 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();

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

(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 = {};
        };
    }
})();

У меня была такая же проблема с использованием ионной структуры (Angular + Cordova). Я знаю, что это не решает проблему, но это код для угловых приложений, основанный на ответах выше. У вас будет эфемерное решение для 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

наслаждайтесь вашей кодировки!


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

в результате 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');
      }
    };
  }
]);

Я только что создал это РЕПО обеспечить 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 to window.sessionStorage если он поддерживается или создает в памяти хранения для SPA, cookie хранения с функциями sesion для non SPA.


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

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

это задокументировано в Safari Technology Preview примечания к выпуску и примечания к выпуску WebKit - для выпуска 29, который был в мае 2017 года.

в частности:

  • исправлено QuotaExceededError при сохранении в localStorage в режиме приватного просмотра или сеансах WebDriver -r215315

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

совместное использование в 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://stackoverflow.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. Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.