web-dev-qa-db-fra.com

QuotaExceededError: Dom exception 22: une tentative d'ajout d'un élément de stockage dépassant le quota a été effectuée.

L'utilisation de LocalStorage sur iPhone avec iOS 7 renvoie cette erreur. Je cherchais une solution, mais vu que je ne navigue même pas en privé, rien n’est pertinent.

Je ne comprends pas pourquoi localStorage serait désactivé par défaut dans iOS 7, mais il semble que ce soit le cas? J'ai également testé sur d'autres sites Web, mais sans succès. J'ai même essayé de le tester en utilisant ce site Web: http://arty.name/localstorage.html , mais il ne semble pas que cela enregistre quoi que ce soit pour une raison étrange.

Quelqu'un at-il eu le même problème, seulement ils ont eu la chance de le réparer? Devrais-je changer de méthode de stockage?

J'ai essayé de le déboguer en ne stockant que quelques lignes d'informations, mais en vain. J'ai utilisé la fonction standard localStorage.setItem() pour enregistrer.

216
NicT

Cela peut se produire lorsque Safari est en navigation en mode privé. Pendant la navigation privée, le stockage local n’est pas disponible du tout.

Une solution consiste à avertir l'utilisateur que l'application a besoin du mode non privé pour fonctionner.

UPDATE: Ce problème a été corrigé dans Safari 11 , le comportement est donc aligné sur celui des autres navigateurs.

365
Cristian Dinu

Comme indiqué dans d'autres réponses, vous obtiendrez toujours l'erreur QuotaExceededError en mode navigateur privé Safari sur iOS et OS X lorsque localStorage.setItem (ou sessionStorage.setItem) est appelé.

Une solution consiste à essayer/attraper ou vérification Modernizr à chaque fois que vous utilisez setItem.

Cependant, si vous voulez une cale qui empêche simplement cette erreur d'être générée, vous pouvez utiliser ceci pour empêcher le reste de votre JavaScript de se rompre:

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

J'utilise cette fonction simple, qui retourne true ou false, pour tester la disponibilité du stockage local:

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

Vous pouvez maintenant tester la disponibilité de localStorage.setItem() avant de l'utiliser. Exemple:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}
16
DrewT

Il m'est arrivé de courir avec le même problème sous iOS 7 (avec certains appareils, pas de simulateurs).

On dirait que Safari dans iOS 7 a un quota de stockage plus faible, qui est apparemment atteint grâce à un long historique.

Je suppose que la meilleure pratique sera d’attraper l’exception.

Le projet Modernizr a un patch facile, vous devriez essayer quelque chose de similaire: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

5
defvol

Voici une solution étendue basée sur la réponse de DrewT ci-dessus qui utilise des cookies si le stockage local n'est pas disponible. Il utilise la bibliothèque 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;
}

Dans votre source, utilisez simplement:

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

Mise à jour (2016-11-01)

J'utilisais AmplifyJS, mentionné ci-dessous, pour résoudre ce problème. Cependant, pour Safari en navigation privée, il s’agissait d’un stockage basé sur la mémoire. Dans mon cas, cela n’était pas approprié car cela signifiait que la mémoire était effacée lors de l’actualisation, même si l’utilisateur était toujours en navigation privée.

De plus, j'ai remarqué un certain nombre d'utilisateurs qui naviguent toujours en mode privé sur iOS Safari. Pour cette raison, un meilleur recours pour Safari consiste à utiliser des cookies (le cas échéant). Par défaut, les cookies sont toujours accessibles même en navigation privée. Bien sûr, ils sont effacés lors de la sortie de la navigation privée, mais ils ne le sont pas lors de l'actualisation.

J'ai trouvé la bibliothèque local-storage-fallback . De la documentation:

Objectif

Avec des paramètres de navigateur tels que "Navigation privée", il est devenu problématique de s’appuyer sur un window.localStorage en état de fonctionnement, même dans les nouveaux navigateurs. Même s'il peut exister, il lancera des exceptions en essayant d'utiliser setItem ou getItem. Ce module effectuera les vérifications appropriées pour voir quel mécanisme de stockage du navigateur pourrait être disponible, puis l'exposer. Elle utilise la même API que localStorage et devrait donc fonctionner comme un remplacement immédiat dans la plupart des cas.

Attention aux pièges:

  • CookieStorage a des limites de stockage. Soyez prudent ici.
  • MemoryStorage ne persistera pas entre les chargements de page. C'est plus ou moins un palliatif pour éviter les plantages de pages, mais cela peut être suffisant pour les sites Web qui ne chargent pas de pages entières.

TL; DR:

Utilisez local-storage-fallback (API unifiée avec .getItem(prop) et .setItem(prop, val)):

Vérifiez et utilisez l'adaptateur de stockage approprié pour le navigateur (stockage local, session, stockage, cookies, mémoire)

Réponse originale

Pour ajouter aux réponses précédentes, une solution de contournement possible serait de changer la méthode de stockage. Quelques librairies telles que AmplifyJS et PersistJS peuvent vous aider. Les deux bibliothèques permettent un stockage persistant côté client via plusieurs serveurs.

Pour AmplifyJS

stockage local

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

sessionStorage

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

globalStorage

  • Firefox 2+

données d'utilisateur

  • IE 5 - 7
  • userData existe également dans les versions les plus récentes de IE, mais en raison des particularités de l'implémentation de IE 9, nous n'enregistrons pas userData si localStorage est pris en charge.

mémoire

  • Un stockage en mémoire est fourni en tant que solution de secours si aucun des autres types de stockage n'est disponible.

Pour PersistentJS

  • flash: stockage persistant de Flash 8.
  • engrenages: stockage persistant basé sur Google Gears.
  • localstorage: stockage de brouillon HTML5.
  • globalstorage: stockage brouillon HTML5 (ancienne spécification).
  • c.-à-d. les comportements des données utilisateur d'Internet Explorer.
  • cookie: stockage persistant basé sur les cookies.

Ils offrent une couche d'abstraction afin que vous n'ayez pas à vous soucier de choisir le type de stockage. Gardez à l'esprit qu'il peut y avoir certaines limitations (telles que les limites de taille) en fonction du type de stockage. Pour le moment, j'utilise AmplifyJS, mais je dois encore faire quelques tests supplémentaires sur iOS 7/Safari/etc. pour voir si cela résout réellement le problème.

2
Jonathan Alzetta

Comme déjà expliqué dans d'autres réponses, en mode de navigation privée , Safari utilisera toujours lève cette exception en essayant de sauvegarder des données avec localStorage.setItem().

Pour résoudre ce problème, j'ai écrit un faux stockage local qui imite localStorage, les méthodes et les événements.

Faux stockage local: https://Gist.github.com/engelfrost/fd707819658f72b42f55

Ce n'est probablement pas une bonne solution générale au problème. C'était une bonne solution pour mon scénario, où l'alternative serait une réécriture majeure vers une application déjà existante.

2
Josef Engelfrost

En avril 2017, un correctif a été intégré à Safari, de sorte qu'il s'aligne sur les autres navigateurs. Ceci a été publié avec Safari 11.

https://bugs.webkit.org/show_bug.cgi?id=15701

1
sandstrom

Cette question et cette réponse m'ont aidé à résoudre un problème spécifique lié à l'inscription de nouveaux utilisateurs dans Parse.

Étant donné que la fonction signUp (attrs, options) utilise la mémoire de stockage locale pour conserver la session, si un utilisateur est en mode de navigation privée, elle lance le message "QuotaExceededError: DOM Exception 22: Une tentative visant à ajouter quelque chose à la mémoire qui dépasse le quota a été effectuée". exception et les fonctions succès/erreur ne sont jamais appelées.

Dans mon cas, comme la fonction d'erreur n'est jamais appelée, elle semblait initialement poser un problème de déclenchement de l'événement click sur la soumission ou de la redirection définie en cas de succès de l'inscription.

L'inclusion d'un avertissement pour les utilisateurs a résolu le problème.

Analyse le code de référence du SDK Javascript https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Inscrivez un nouvel utilisateur avec un nom d'utilisateur (ou email) et un mot de passe. Cela créera un nouveau Parse.User sur le serveur et persistera la session dans localStorage afin que vous puissiez accéder à l'utilisateur à l'aide de {@link #current }.

0
clayostrom