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

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

JavaScript: ошибка

undefined

QUOTA_EXCEEDED_ERR: DOM Exception 22: Была сделана попытка добавить что-то в память...

мой код:

localStorage.setItem('test',1)

Ответ 1

По-видимому, это по дизайну. Когда 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."

Что происходит, так это то, что объект окна по-прежнему предоставляет 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;
    }
}

Ответ 2

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

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

Ответ 3

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

Одним из решений является попытка try/catch или Проверка модернизма в каждом случае использования 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.');
    }
}

Ответ 4

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

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

В моем коде я никогда не вызываю localStorage напрямую. Я вызываю cusSto глобальный 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();

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

Здесь решение для 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');
      }
    };
  }
]);

Ответ 8

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

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

  • IE5 +
  • Chrome все версии
  • Mozilla все версии
  • Yandex все версии

Как это работает

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

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.

Ответ 9

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

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

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

В частности:

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

Ответ 10

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

совместное использование в 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);

Это позволит убедиться, что ваши ключи установлены и правильно загружены во всех браузерах.

Ответ 11

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

Ответ 12

Вот сервисная версия 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);
    }

}

Ответ 13

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

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