Errore html5 localStorage con Safari: “QUOTA_EXCEEDED_ERR: DOM Eccezione 22: è stato effettuato un tentativo di aggiungere qualcosa all’archivio che superava la quota.”

La mia webapp ha errori javascript nella navigazione privata di ios safari:

JavaScript: Errore

non definito

QUOTA_EXCEEDED_ERR: DOM Exception 22: tentativo di aggiungere qualcosa allo storage …

il mio codice:

localStorage.setItem('test',1) 

Apparentemente questo è di design. Quando Safari (OS X o iOS) è in modalità di navigazione privata, sembra che localStorage sia disponibile, ma provare a chiamare setItem genera un’eccezione.

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

Quello che succede è che l’object window espone ancora localStorage nello spazio dei nomi globale, ma quando si chiama setItem , viene generata questa eccezione. Qualsiasi chiamata a removeItem viene ignorata.

Credo che la soluzione più semplice (sebbene non abbia ancora provato questo cross browser) sarebbe quella di alterare la funzione isLocalStorageNameSupported() per verificare che si possa anche impostare un valore.

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; } } 

La correzione pubblicata sul link sopra non ha funzionato per me. Questo ha fatto:

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

Derivato da http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5

Come accennato in altre risposte, otterrete sempre il QuotaExceededError nella modalità Browser privato di Safari su iOS e OS X quando viene chiamato localStorage.setItem (o sessionStorage.setItem ).

Una soluzione è eseguire un controllo try / catch o Modernizr in ogni istanza di utilizzo di setItem .

Tuttavia, se vuoi uno spessore che blocchi globalmente questo errore, per evitare che il resto del tuo JavaScript si rompa, puoi usare questo:

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.'); } } 

Nel mio contesto, ho appena sviluppato un’astrazione di class. Quando viene avviata la mia applicazione, controllo se localStorage funziona chiamando getStorage () . Anche questa funzione restituisce:

  • localStorage se localStorage funziona
  • o un’implementazione di una class personalizzata LocalStorageAlternative

Nel mio codice non ho mai chiamato localStorage direttamente. Chiamo cusSto global var, l’ho inizializzato chiamando getStorage () .

In questo modo, funziona con la navigazione privata o specifiche versioni di 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(); 

Per espandere le risposte degli altri, ecco una soluzione compatta che non espone / aggiunge nuove variabili. Non copre tutte le basi, ma dovrebbe essere adatto alla maggior parte delle persone che desiderano solo un’app per pagina singola per rimanere funzionale (nonostante la persistenza dei dati dopo la ricarica).

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

Ho avuto lo stesso problema usando la struttura ionica (Angular + Cordova). So che questo non risolve il problema, ma è il codice per le app angolari basato sulle risposte sopra. Avrai una soluzione effimera per localStorage nella versione iOS di Safari.

Ecco il codice:

 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); } } } ]); 

Fonte: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871

Buona lettura!

Ecco una soluzione per AngularJS che utilizza un IIFE e fa leva sul fatto che i servizi sono singleton .

Ciò comporta che isLocalStorageAvailable sia impostato immediatamente quando il servizio viene iniettato per la prima volta ed evita di eseguire inutilmente il controllo ogni volta che è necessario accedere all’archivio locale.

 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'); } }; } ]); 

Ho appena creato questo repository per fornire le sessionStorage e localStorage per i browser non supportati o disabilitati.

Browser supportati

  • IE5 +
  • Chrome tutte le versioni
  • Mozilla tutte le versioni
  • Yandex tutte le versioni

Come funziona

Rileva la funzione con il tipo di archiviazione.

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

Imposta window.localStorage su window.localStorage se è supportato o crea una memorizzazione dei cookie. Imposta StorageService.sessionStorage su window.sessionStorage se è supportato o crea una memoria in memoria per SPA, memorizzazione dei cookie con funzioni sesion per non SPA.

Sembra che Safari 11 cambi il comportamento e ora l’archiviazione locale funzioni in una finestra del browser privata. Evviva!

La nostra app Web che prima falliva nella navigazione privata di Safari ora funziona perfettamente. Ha sempre funzionato bene nella modalità di navigazione privata di Chrome, che ha sempre consentito la scrittura su memoria locale.

Questo è documentato nelle note sulla versione di Safari Technology Preview di Apple – e nelle note di rilascio di WebKit – per la release 29, che era in maggio 2017.

In particolare:

  • Risolto QuotaExceededError durante il salvataggio in localStorage in modalità di navigazione privata o sessioni WebDriver – r215315

Non usarlo se non supportato e per controllare il supporto chiama questa funzione

condivisione in Es6 full read e write localStorage Esempio con controllo dell’assistenza

 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); 

Ciò assicurerà che le tue chiavi siano impostate e recuperate correttamente su tutti i browser.

Il seguente script ha risolto il mio problema:

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

Controlla se esiste localStorage e può essere utilizzato e, in caso negativo, crea una memoria locale falsa e la utilizza al posto del localStorage originale. Per favore fatemi sapere se avete bisogno di ulteriori informazioni.