QuotaExceededError: Dom exceção 22: Foi feita uma tentativa de adicionar algo ao armazenamento que excedeu a cota

219

O uso do LocalStorage no iPhone com iOS 7 gera esse erro. Estive procurando um resolvente, mas, considerando que nem estou navegando em particular, nada é relevante.

Não entendo por que o localStorage seria desativado por padrão no iOS 7, mas parece que é? Também testei em outros sites, mas sem sorte. Até tentei testá-lo usando este site: http://arty.name/localstorage.html , mas não parece que ele esteja salvando nada por algum motivo estranho.

Alguém já teve o mesmo problema, mas teve sorte em consertá-lo? Devo mudar meu método de armazenamento?

Tentei fazer uma depuração difícil armazenando apenas algumas linhas de informação, mas sem sucesso. Eu usei a localStorage.setItem()função padrão para salvar.

Nict
fonte
2
Geralmente, isso significa que você tentou armazenar algo com um tamanho que excedia o espaço de armazenamento disponível. Qual navegador você está usando (Safari, Chrome etc.)? Você pode compartilhar um pouco mais do código que está usando e, se possível, dos dados que está tentando armazenar.
3
Isso deve ser considerado um bug ou problema no lado do Safari. Não faz sentido que você não pode usar localStorage no modo anônimo ...
Maksim Luzik
Use um recurso que detecta testes para esse problema específico . Se o armazenamento não estiver disponível, considere calçar localStorage com memoryStorage . Disclaimer: Eu sou o autor dos pacotes ligados
Stijn de Witt
1
Em abril de 2017, um patch foi mesclado no Safari, alinhado com os outros navegadores. Provavelmente chegará
sandstrom
2
Posso confirmar que isso foi corrigido no Safari iOS 11. Testado Navegação privada + sessionStorage.setItem () e sessionStorage.getItem () com êxito no iPhone6 ​​e iPhone8.
precisa

Respostas:

372

Isso pode ocorrer quando o Safari está na navegação em modo privado. Enquanto estiver navegando em particular, o armazenamento local não está disponível.

Uma solução é avisar o usuário que o aplicativo precisa do modo não privado para funcionar.

ATUALIZAÇÃO: Isso foi corrigido no Safari 11 , então o comportamento agora está alinhado com outros navegadores.

Cristian Dinu
fonte
4
Sua postagem foi incrivelmente útil e oportuna para mim hoje (menos de 24 horas depois). Para referência, aqui está como ligar / desligar a navegação privada: imore.com/how-use-private-browsing-ios-7-safari
Nick
12
+1 corrigiu meu problema. Eu estava verificando a existência de LocalStorage ( if( typeof Storage != 'undefined' ) { ... }) antes de tentar carregar e salvar informações, mas obtendo esse erro. Acontece que Storageainda está definido, mesmo quando é inutilizável. Usando try / catch a partir de agora, sempre que eu usar o LocalStorage.
stevendesu
Obrigado! Erro estranho no safari. Deveria ter sido mais informativo. : D
Sunny R Gupta
2
Uma correção pode ser recebida a partir do Safari Tech Preview 29: "Corrigido QuotaExceededError ao salvar no localStorage no modo de navegação privada ou nas sessões do WebDriver". Veja developer.apple.com/safari/technology-preview/release-notes
Marc Baumbach
1
Isso também pode ocorrer se o limite de armazenamento for alcançado, o que pode ser feito facilmente salvando imagens, por exemplo.
Csalmeida # 19/17
103

Conforme mencionado em outras respostas, você sempre obterá o QuotaExceededError no modo Navegador privado do Safari no iOS e no 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 de uso setItem.

No entanto, se você quiser um calço que simplesmente impeça a execução desse erro globalmente, para impedir que o restante do JavaScript seja interrompido, você pode usar o seguinte:

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.');
    }
}
philfreo
fonte
1
Por que adicionar o setItem ao objeto Storage se você não poderá usá-lo de qualquer maneira?
Necromancer
4
O objetivo do meu snippet é simplesmente ignorar os erros JS de serem lançados se você quiser que seu aplicativo não seja totalmente quebrado no modo privado do Safari.
23416 philfreo
16

Eu uso essa função simples, que retorna trueou false, para testar a disponibilidade do localStorage:

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

Agora você pode testar a localStorage.setItem()disponibilidade antes de usá-lo. Exemplo:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}
DrewT
fonte
Perdi alguma coisa? Por que window.sessionStoragefoi usado em vez de window.localStoragepara um método chamado isLocalStorageNameSupported?
Ithar 29/03
@lthar - consulte a documentação aqui: w3schools.com/html/html5_webstorage.asp O mais importante é esta parte:HTML local storage provides two objects for storing data on the client: window.localStorage - stores data with no expiration date window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)
DrewT
@ DrewT, mas qual é a diferença nessa situação se você remover sua chave de teste? Não importa onde armazenarei minha chave de teste se vou excluí-la. Estou errado? Por que o armazenamento da sessão é melhor que o local?
Vladyslav Turak
1
@TurakVladyslav, você está certo. Não há realmente nenhuma diferença aqui, exceto que o uso o sessionStoragetorna mais gerenciável para definir pontos de interrupção, se você quiser testar seu desenvolvimento. Não há argumento verdadeiro para o que é "melhor" e é realmente apenas uma preferência pessoal que erra do lado da cautela. O principal a observar é que ambas sessionStoragee localStoragesão implementações da API de armazenamento da web HTML5.
DrewT
5

Por acaso corri com o mesmo problema no iOS 7 (com alguns dispositivos sem simuladores).

Parece que o Safari no iOS 7 tem uma cota de armazenamento mais baixa, que aparentemente é atingida por um longo histórico de log.

Acho que a melhor prática será capturar a exceção.

O projeto Modernizr tem um patch fácil, você deve tentar algo semelhante: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

defvol
fonte
3

Aqui está uma solução expandida com base na resposta de DrewT acima que usa cookies se localStorage não estiver disponível. Ele usa a biblioteca docCookies do Mozilla :

function localStorageGet( pKey ) {
    if( localStorageSupported() ) {
        return localStorage[pKey];
    } else {
        return docCookies.getItem( 'localstorage.'+pKey );
    }
}

function localStorageSet( pKey, pValue ) {
    if( localStorageSupported() ) {
        localStorage[pKey] = pValue;
    } else {
        docCookies.setItem( 'localstorage.'+pKey, pValue );
    }
}

// global to cache value
var gStorageSupported = undefined;
function localStorageSupported() {
    var testKey = 'test', storage = window.sessionStorage;
    if( gStorageSupported === undefined ) {
        try {
            storage.setItem(testKey, '1');
            storage.removeItem(testKey);
            gStorageSupported = true;
        } catch (error) {
            gStorageSupported = false;
        }
    }
    return gStorageSupported;
}

Na sua fonte, basta usar:

localStorageSet( 'foobar', 'yes' );
...
var foo = localStorageGet( 'foobar' );
...
Stickley
fonte
2

Como já explicado em outras respostas, quando estiver no modo Navegação privada, o Safari sempre lançará essa exceção ao tentar salvar dados comlocalStorage.setItem() .

Para corrigir isso, escrevi um localStorage falso que imita o localStorage, métodos e eventos.

Local falsoStorage: https://gist.github.com/engelfrost/fd707819658f72b42f55

Provavelmente, essa não é uma boa solução geral para o problema. Essa foi uma boa solução para o meu cenário, onde a alternativa seria reescrever muito em um aplicativo já existente.

Josef Engelfrost
fonte
O que exatamente isso corrige? Não persiste nada, então qual é o objetivo?
Esben Skov Pedersen
1
Ele "corrige" o Safari quando no modo de navegação privada. (Isso não está claro na minha resposta, obrigado por apontar isso. Vou editar minha resposta). Nada deve persistir no modo de navegação privada, portanto, não persistir não é um problema relevante aqui. O que isso corrigiu para mim foi permitir que os usuários executassem um aplicativo já existente, sem grandes regravações, mesmo no modo de Navegação Privada no Safari.
Josef Engelfrost
2

Atualização (01/11/2016)

Eu estava usando o AmplifyJS mencionado abaixo para solucionar esse problema. No entanto, para o Safari na navegação privada, ele voltava a um armazenamento baseado em memória. No meu caso, não era apropriado porque significa que o armazenamento é limpo na atualização, mesmo se o usuário ainda estiver na navegação privada.

Além disso, notei vários usuários que estão sempre navegando no modo Privado no iOS Safari. Por esse motivo, uma alternativa melhor para o Safari é usar cookies (se disponível). Por padrão, os cookies ainda estão acessíveis, mesmo na navegação privada. Obviamente, eles são limpos ao sair da navegação privada, mas não são limpos na atualização.

Encontrei a biblioteca local-storage-fallback . A partir da documentação:

Objetivo

Com configurações do navegador como "Navegação privada", tornou-se um problema depender de uma janela de trabalho.localStorage, mesmo em navegadores mais recentes. Mesmo que exista, ele lançará exceções ao tentar usar setItem ou getItem. Este módulo executará verificações apropriadas para ver qual mecanismo de armazenamento do navegador pode estar disponível e, em seguida, expô-lo. Ele usa a mesma API que localStorage, portanto, na maioria dos casos, ele funciona como um substituto de entrada.

Cuidado com as dicas:

  • O CookieStorage possui limites de armazenamento. Tenha cuidado aqui.
  • O MemoryStorage não persistirá entre os carregamentos da página. Isso é mais ou menos um intervalo para evitar falhas de página, mas pode ser suficiente para sites que não carregam páginas inteiras.

TL; DR:

Use local-storage-fallback (API unificada com .getItem(prop)e .setItem(prop, val)):

Verifique e use o adaptador de armazenamento apropriado para o navegador (armazenamento local, armazenamento de sessão, cookies, memória)

Resposta original

Para adicionar respostas anteriores, uma solução possível seria alterar o método de armazenamento. Existem algumas bibliotecas como AmplifyJS e PersistJS que podem ajudar. Ambas as bibliotecas permitem armazenamento persistente do lado do cliente através de vários back-ends.

Para AmplifyJS

localStorage

  • IE 8+
  • Firefox 3.5 ou superior
  • Safari 4+
  • cromada
  • Opera 10.5+
  • iPhone 2+
  • Android 2 ou superior

sessionStorage

  • IE 8+
  • Firefox 2+
  • Safari 4+
  • cromada
  • Opera 10.5+
  • iPhone 2+
  • Android 2 ou superior

globalStorage

  • Firefox 2+

dados do usuário

  • IE 5 - 7
  • O userData também existe nas versões mais recentes do IE, mas devido a peculiaridades na implementação do IE 9, não registramos userData se o localStorage for suportado.

memória

  • Um armazenamento na memória é fornecido como um substituto se nenhum dos outros tipos de armazenamento estiver disponível.

For PersistentJS

  • flash: armazenamento persistente do Flash 8.
  • engrenagens: armazenamento persistente baseado no Google Gears.
  • localstorage: armazenamento de rascunho em HTML5.
  • globalstorage: armazenamento de rascunho em HTML5 (especificação antiga).
  • ou seja: comportamentos de dados do usuário do Internet Explorer.
  • cookie: armazenamento persistente baseado em cookie.

Eles oferecem uma camada de abstração para que você não precise se preocupar em escolher o tipo de armazenamento. Lembre-se de que pode haver algumas limitações (como limites de tamanho), dependendo do tipo de armazenamento. No momento, estou usando o AmplifyJS, mas ainda preciso fazer mais testes no iOS 7 / Safari / etc. para ver se ele realmente resolve o problema.

Jonathan Alzetta
fonte
Editor John: Eu sei que você e Jonathan Alzetta provavelmente têm a mesma conta e você está apenas tentando melhorar sua resposta, mas, nesse caso, você deve realmente fazer login como Jonathan Alzetta e editar essa resposta, para que não ocorra. a fila de revisão. Recupere sua conta, se necessário.
Davids
0

Esta pergunta e resposta me ajudaram a resolver um problema específico com a inscrição de novos usuários no Parse.

Como a função signUp (attrs, options) usa armazenamento local para manter a sessão, se um usuário estiver no modo de navegação privada, ele lança a "QuotaExceededError: DOM Exceção 22: Foi feita uma tentativa de adicionar algo ao armazenamento que excedeu a cota". A exceção e as funções de sucesso / erro nunca são chamadas.

No meu caso, como a função de erro nunca é chamada, inicialmente parecia haver um problema ao disparar o evento click no envio ou no redirecionamento definido no sucesso da inscrição.

A inclusão de um aviso para os usuários resolveu o problema.

Analisar referência do SDK Javascript https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Registra um novo usuário com um nome de usuário (ou email) e senha. Isso criará um novo Parse.User no servidor e também persistirá a sessão em localStorage, para que você possa acessar o usuário usando {@link #current}.

clayostrom
fonte