QuotaExceededError: Dom excepción 22: se intentó agregar algo al almacenamiento que superó la cuota

219

El uso de LocalStorage en iPhone con iOS 7 arroja este error. He estado buscando un resolutivo, pero considerando que ni siquiera estoy navegando en privado, nada es relevante.

No entiendo por qué localStorage estaría deshabilitado por defecto en iOS 7, pero parece que sí. También lo he probado en otros sitios web, pero sin suerte. Incluso intenté probarlo usando este sitio web: http://arty.name/localstorage.html , pero no parece que esté guardando nada en absoluto por alguna extraña razón.

¿Alguien ha tenido el mismo problema, solo que han tenido la suerte de solucionarlo? ¿Debo cambiar mi método de almacenamiento?

Traté de depurarlo almacenando unas pocas líneas de información, pero fue en vano. Usé la localStorage.setItem()función estándar para guardar.

Nict
fuente
2
Por lo general, significa que intentó almacenar algo con un tamaño que excedió el espacio de almacenamiento disponible. ¿Qué navegador estás usando (Safari, Chrome, etc.)? ¿Puede compartir un poco más del código que ha estado utilizando y, si es posible, los datos que está tratando de almacenar?
3
Esto debe considerarse como un error o problema en el lado de Safari. No tiene sentido que no pueda usar localStorage en modo incógnito ...
Maksim Luzik
Utilice una función de detección que prueba este problema específico . Si el almacenamiento no está disponible, considere shimming localStorage con memoryStorage . descargo de responsabilidad: soy el autor de los paquetes vinculados
Stijn de Witt
1
En abril de 2017, un parche se fusionó con Safari, por lo que se alineó con los otros navegadores. Probablemente aterrizará en Safari 11. bugs.webkit.org/show_bug.cgi?id=157010
sandstrom
2
Puedo confirmar que esto se ha solucionado en Safari iOS 11. Probado Navegación privada + sessionStorage.setItem () luego sessionStorage.getItem () con éxito en iPhone6 ​​y iPhone8.
Kevin Gaudin

Respuestas:

372

Esto puede ocurrir cuando Safari está en modo privado de navegación. Durante la navegación privada, el almacenamiento local no está disponible en absoluto.

Una solución es advertir al usuario que la aplicación necesita un modo no privado para funcionar.

ACTUALIZACIÓN: Esto se ha solucionado en Safari 11 , por lo que el comportamiento ahora está alineado con otros navegadores.

Cristian Dinu
fuente
44
Su publicación fue increíblemente útil y oportuna para mí hoy (menos de 24 horas después). Como referencia, aquí se explica cómo activar / desactivar la navegación privada: imore.com/how-use-private-browsing-ios-7-safari
Nick
12
+1 solucionó mi problema. Estaba comprobando la existencia de LocalStorage ( if( typeof Storage != 'undefined' ) { ... }) antes de intentar cargar y guardar información, pero recibí este error. Resulta que Storagetodavía está definido, incluso cuando es inutilizable. Usando try / catch de ahora en adelante cada vez que uso LocalStorage.
stevendesu
¡Gracias! Extraño error de safari. Debería haber sido más informativo. : D
Sunny R Gupta
2
Puede haber una solución entrante a partir de Safari Tech Preview 29: "Se corrigió QuotaExceededError al guardar en localStorage en modo de navegación privada o sesiones de WebDriver". Ver developer.apple.com/safari/technology-preview/release-notes
Marc Baumbach
1
Esto también puede ocurrir si se alcanza el límite de almacenamiento, lo que se puede hacer fácilmente guardando imágenes, por ejemplo.
csalmeida
103

Como se menciona en otras respuestas, siempre obtendrá QuotaExceededError en Safari Private Browser Mode en iOS y OS X cuando se llama localStorage.setItem(o sessionStorage.setItem).

Una solución es hacer una prueba try / catch o Modernizr en cada instancia de uso setItem.

Sin embargo, si desea una cuña que simplemente detenga globalmente este error, para evitar que el resto de su JavaScript se rompa, puede usar esto:

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
fuente
1
¿Por qué agregar setItem al objeto Storage si de todos modos no podrá usarlo?
Nigromante
44
El objetivo de mi fragmento es simplemente ignorar los errores de JS de ser arrojados si desea que su aplicación no se rompa por completo en el modo privado Safari.
philfreo
16

Utilizo esta función simple, que devuelve trueo false, para probar la disponibilidad de almacenamiento local:

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

Ahora puede probar la localStorage.setItem()disponibilidad antes de usarlo. Ejemplo:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}
DrewT
fuente
¿Me he perdido algo? ¿Por qué se window.sessionStorageusa en lugar de window.localStoragepara un método llamado isLocalStorageNameSupported?
Ithar
@lthar - vea la documentación aquí: w3schools.com/html/html5_webstorage.asp Lo más importante de 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, pero ¿cuál es la diferencia en esta situación si elimina la clave de prueba? No importa dónde guardaré mi clave de prueba si la voy a eliminar. ¿Me equivoco? ¿Por qué el almacenamiento de la sesión es mejor que el local?
Vladyslav Turak
1
@TurakVladyslav tiene razón, realmente no hay diferencia aquí, excepto que el uso lo sessionStoragehace más manejable para establecer puntos de interrupción si desea probar su desarrollo. No existe un argumento verdadero para cuál es "mejor" y en realidad es solo una preferencia personal aquí que se equivoca por el lado de la precaución. Lo principal a tener en cuenta es que ambas sessionStoragey localStorageambas son implementaciones de la API de almacenamiento web HTML5.
DrewT
3

Aquí hay una solución ampliada basada en la respuesta anterior de DrewT que usa cookies si localStorage no está disponible. Utiliza la biblioteca docCookies de 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;
}

En su fuente, simplemente use:

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

Como ya se explicó en otras respuestas, cuando está en modo de Navegación Privada, Safari siempre lanzará esta excepción cuando intente guardar datos con localStorage.setItem().

Para solucionar esto, escribí un localStorage falso que imita localStorage, tanto métodos como eventos.

Almacenamiento local falso: https://gist.github.com/engelfrost/fd707819658f72b42f55

Probablemente esta no sea una buena solución general al problema. Esta fue una buena solución para mi escenario, donde la alternativa sería la reescritura de una aplicación ya existente.

Josef Engelfrost
fuente
¿Qué soluciona exactamente? No persiste nada, entonces, ¿cuál es el punto?
Esben Skov Pedersen
1
"Corrige" Safari cuando está en modo de navegación privada. (Esto no está claro en mi respuesta, gracias por señalarlo. Editaré mi respuesta). Se supone que nada debe persistir cuando se está en modo de navegación privada, por lo que no persistir no es un problema relevante aquí. Lo que esto solucionó para mí fue permitir a los usuarios ejecutar una aplicación ya existente, sin grandes reescrituras, incluso en modo de navegación privada en Safari.
Josef Engelfrost
2

Actualización (2016-11-01)

Estaba usando AmplifyJS mencionado a continuación para solucionar este problema. Sin embargo, para Safari en la navegación privada, se estaba volviendo a un almacenamiento basado en memoria. En mi caso, no era apropiado porque significa que el almacenamiento se borra al actualizar, incluso si el usuario todavía está en navegación privada.

Además, he notado una cantidad de usuarios que siempre están navegando en modo privado en iOS Safari. Por esa razón, una mejor alternativa para Safari es usar cookies (si están disponibles). Por defecto, las cookies siguen siendo accesibles incluso en la navegación privada. Por supuesto, se borran al salir de la navegación privada, pero no se borran al actualizar.

Encontré la biblioteca local de almacenamiento de reserva . De la documentación:

Propósito

Con configuraciones del navegador como "Navegación privada", se ha convertido en un problema confiar en una ventana de trabajo. LocalStorage, incluso en los navegadores más nuevos. Aunque pueda existir, arrojará excepciones cuando intente usar setItem o getItem. Este módulo ejecutará las verificaciones adecuadas para ver qué mecanismo de almacenamiento del navegador podría estar disponible y luego lo expondrá. Utiliza la misma API que localStorage, por lo que debería funcionar como un reemplazo directo en la mayoría de los casos.

Cuidado con las gotchas:

  • CookieStorage tiene límites de almacenamiento. Ten cuidado aquí.
  • MemoryStorage no persistirá entre cargas de página. Esto es más o menos un espacio intermedio para evitar bloqueos de página, pero puede ser suficiente para sitios web que no realizan cargas de página completa.

TL; DR:

Utilice el almacenamiento local alternativo (API unificada con .getItem(prop)y .setItem(prop, val)):

Verifique y use el adaptador de almacenamiento apropiado para el navegador (localStorage, sessionStorage, cookies, memory)

Respuesta original

Para agregar respuestas anteriores, una posible solución sería cambiar el método de almacenamiento. Hay algunas bibliotecas como AmplifyJS y PersistJS que pueden ayudar. Ambas bibliotecas permiten el almacenamiento persistente del lado del cliente a través de varios backends.

Para AmplifyJS

almacenamiento local

  • IE 8+
  • Firefox 3.5+
  • Safari 4+
  • Cromo
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

sessionStorage

  • IE 8+
  • Firefox 2+
  • Safari 4+
  • Cromo
  • Opera 10.5+
  • iPhone 2+
  • Android 2+

globalStorage

  • Firefox 2+

datos del usuario

  • IE 5 - 7
  • userData también existe en versiones más nuevas de IE, pero debido a las peculiaridades en la implementación de IE 9, no registramos userData si se admite localStorage.

memoria

  • Se proporciona un almacén en memoria como respaldo si ninguno de los otros tipos de almacenamiento está disponible.

Para PersistentJS

  • flash: almacenamiento persistente de Flash 8.
  • engranajes: almacenamiento persistente basado en Google Gears.
  • localstorage: almacenamiento de borradores HTML5.
  • almacenamiento global: almacenamiento de borradores HTML5 (especificación anterior).
  • es decir: comportamientos de datos de usuario de Internet Explorer.
  • cookie: almacenamiento persistente basado en cookies.

Ofrecen una capa de abstracción para que no tenga que preocuparse por elegir el tipo de almacenamiento. Sin embargo, tenga en cuenta que puede haber algunas limitaciones (como límites de tamaño) dependiendo del tipo de almacenamiento. En este momento, estoy usando AmplifyJS, pero aún tengo que hacer más pruebas en iOS 7 / Safari / etc. para ver si realmente resuelve el problema.

Jonathan Alzetta
fuente
Editor John: Me doy cuenta de que usted y Jonathan Alzetta son probablemente la misma cuenta y solo está tratando de mejorar su respuesta, pero si es así, realmente debe iniciar sesión como Jonathan Alzetta y editar esta respuesta, y luego no pasará. La cola de revisión. Recupere su cuenta si es necesario.
DavidS
0

Esta pregunta y respuesta me ayudaron a resolver un problema específico al registrar nuevos usuarios en Parse.

Debido a que la función signUp (attrs, options) usa almacenamiento local para persistir la sesión, si un usuario está en modo de navegación privada, arroja el "QuotaExceededError: DOM Exception 22: Se intentó agregar algo al almacenamiento que excedió la cuota". excepción y las funciones de éxito / error nunca se llaman.

En mi caso, debido a que la función de error nunca se llama, inicialmente parecía ser un problema al activar el evento de clic en el envío o la redirección definida en el éxito del registro.

La inclusión de una advertencia para los usuarios resolvió el problema.

Referencia de Parse Javascript SDK https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Registra a un nuevo usuario con un nombre de usuario (o correo electrónico) y contraseña. Esto creará un nuevo Parse.User en el servidor y también persistirá la sesión en localStorage para que pueda acceder al usuario utilizando {@link #current}.

clayostrom
fuente