html5 erro localStorage com o Safari: “QUOTA_EXCEEDED_ERR: DOM Exceção 22: Foi feita uma tentativa de adicionar algo ao armazenamento que excedeu a cota.”

Meu webapp tem erros de javascript em ios safari browsing:

JavaScript: erro

Indefinido

QUOTA_EXCEEDED_ERR: DOM Exceção 22: Foi feita uma tentativa de adicionar algo ao armazenamento …

meu código:

localStorage.setItem('test',1) 

Aparentemente isso é por design. Quando o Safari (OS X ou iOS) está no modo de navegação privada, parece que localStorage está disponível, mas tentar chamar setItem gera uma exceção.

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

O que acontece é que o object window ainda expõe localStorage no namespace global, mas quando você chama setItem , essa exceção é lançada. Todas as chamadas para removeItem são ignoradas.

Acredito que a correção mais simples (embora eu ainda não tenha testado este cross browser) seria alterar a function isLocalStorageNameSupported() para testar se você também pode definir algum valor.

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

A correção postada no link acima não funcionou para mim. Isso fez:

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

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

Como mencionado em outras respostas, você sempre obterá o QuotaExceededError no modo de navegador privado do Safari no iOS e OS X quando localStorage.setItem (ou sessionStorage.setItem ) for chamado.

Uma solução é fazer uma verificação try / catch ou Modernizr em cada instância do uso de setItem .

No entanto, se você quiser um shim que simplesmente interrompa globalmente esse erro, para evitar que o restante do JavaScript seja corrompido, use-o:

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

No meu contexto, acabei de desenvolver uma abstração de class. Quando meu aplicativo é iniciado, eu verifico se localStorage está funcionando chamando getStorage () . Esta function também retorna:

  • localStorage se localStorage estiver funcionando
  • ou uma implementação de uma class customizada LocalStorageAlternative

No meu código eu nunca chamo o localStorage diretamente. Eu chamo cusSto global var, eu tinha inicializado chamando getStorage () .

Desta forma, funciona com navegação privada ou versões específicas do 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(); 

Para expandir as respostas dos outros, aqui está uma solução compacta que não expõe / adiciona novas variables. Ele não cobre todas as bases, mas deve ser adequado para a maioria das pessoas que querem apenas que um aplicativo de página única permaneça funcional (apesar de não haver persistência de dados após o recarregamento).

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

Eu tive o mesmo problema usando o framework Ionic (Angular + Cordova). Eu sei que isso não resolve o problema, mas é o código do Angular Apps baseado nas respostas acima. Você terá uma solução efêmera para o localStorage na versão para iOS do Safari.

Aqui está o código:

 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

Aproveite a sua codificação!

Aqui está uma solução para AngularJS usando um IIFE e aproveitando o fato de que os serviços são singletons .

Isso resulta em isLocalStorageAvailable sendo definido imediatamente quando o serviço é injetado pela primeira vez e evita a execução desnecessária da verificação toda vez que o armazenamento local precisar ser acessado.

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

Acabei de criar este repository para fornecer resources sessionStorage e localStorage para navegadores não suportados ou desativados.

Navegadores Suportados

  • IE5 +
  • Chrome todas as versões
  • Mozilla todas as versões
  • Yandex todas as versões

Como funciona

Ele detecta o recurso com o tipo de armazenamento.

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

Define StorageService.localStorage como window.localStorage se ele for suportado ou se criar um armazenamento de cookie. Define StorageService.sessionStorage como window.sessionStorage se ele for compatível ou criar um armazenamento na memory para SPA, armazenamento de cookie com resources sesion para non SPA.

Parece que o Safari 11 altera o comportamento e agora o armazenamento local funciona em uma janela privada do navegador. Viva!

Nosso aplicativo da web que costumava falhar na navegação privada do Safari agora funciona perfeitamente. Ele sempre funcionava bem no modo de navegação privada do Chrome, que sempre permitia gravar no armazenamento local.

Isso está documentado nas notas de versão do Safari Technology Preview da Apple – e nas notas de versão do WebKit – para o release 29, que foi em maio de 2017.

Especificamente:

  • Corrigido QuotaExceededError ao salvar em localStorage em modo de navegação privada ou sessões do WebDriver – r215315

Não usá-lo se não for suportado e para verificar o suporte basta chamar essa function

compartilhando em Es6 leia e grave completo localStorage Exemplo com verificação de suporte

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

Isso garantirá que suas chaves sejam definidas e recuperadas corretamente em todos os navegadores.

O script a seguir resolveu meu 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(); } 

Ele verifica se localStorage existe e pode ser usado e, no caso negativo, cria um armazenamento local falso e o utiliza em vez do localStorage original. Por favor, deixe-me saber se você precisar de mais informações.

Intereting Posts