web-dev-qa-db-fra.com

erreur html5 localStorage avec Safari: "QUOTA_EXCEEDED_ERR: Exception DOM 22: une tentative d’ajout d’un élément de stockage dépassant le quota a été effectuée."

Ma webapp a des erreurs javascript dans la navigation privée ios safari:

JavaScript: erreur

undefined

QUOTA_EXCEEDED_ERR: Exception DOM 22: une tentative a été faite pour ajouter quelque chose au stockage ...

mon code:

localStorage.setItem('test',1)
131
leiyonglin

Apparemment, c'est par conception. Lorsque Safari (OS X ou iOS) est en mode de navigation privée, localStorage est disponible, mais le fait d'appeler setItem lève une exception.

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

En réalité, l'objet window expose toujours localStorage dans l'espace de noms global, mais lorsque vous appelez setItem, cette exception est levée. Tous les appels à removeItem sont ignorés.

Je pense que la solution la plus simple (même si je n’ai pas encore testé ce navigateur) serait de modifier la fonction isLocalStorageNameSupported() pour vérifier que vous pouvez également définir une valeur.

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

Le correctif affiché sur le lien ci-dessus ne fonctionnait pas pour moi. Cela a:

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

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

37
cyberwombat

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 Modernizr check dans chaque cas d'utilisation de setItem.

Cependant, si vous voulez un shim qui bloque simplement cette erreur de manière globale, 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.');
    }
}
24
philfreo

Dans mon contexte, je viens de développer une abstraction de classe . Lorsque mon application est lancée, je vérifie si localStorage fonctionne en appelant getStorage () . Cette fonction retourne également:

  • soit localStorage si localStorage fonctionne
  • ou une implémentation d'une classe personnalisée LocalStorageAlternative

Dans mon code, je n'appelle jamais directement localStorage. J'appelle cusSto global var, j'avais initialisé en appelant getStorage () .

De cette façon, il fonctionne avec la navigation privée ou avec des versions spécifiques de 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();
11
Pierre Le Roux

Pour développer les réponses des autres, voici une solution compacte qui n’expose/n’ajoute aucune nouvelle variable. Il ne couvre pas toutes les bases, mais il convient à la plupart des gens qui souhaitent simplement qu'une application d'une seule page reste fonctionnelle (malgré l'absence de persistance des données après le rechargement).

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

J'ai eu le même problème en utilisant Ionic Framework (Angular + Cordova). Je sais que cela ne résout pas le problème, mais c'est le code pour Angular Apps basé sur les réponses ci-dessus. Vous aurez une solution éphémère pour localStorage sur la version iOS de Safari.

Voici le code:

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

Source: https://Gist.github.com/jorgecasar/61fda6590dc2bb17e871

Profitez de votre codage!

3
jorgecasar

Voici une solution pour AngularJS utilisant un IIFE et utilisant le fait que les services sont des singletons .

Cela entraîne que isLocalStorageAvailable est défini immédiatement lors de la première injection du service et évite d'exécuter inutilement la vérification à chaque fois qu'il est nécessaire d'accéder à la mémoire de stockage 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');
      }
    };
  }
]);
2
Pier-Luc Gendreau

Je viens de créer ce repo pour fournir les fonctions sessionStorage et localStorage à des navigateurs non pris en charge ou désactivés.

Navigateurs pris en charge

  • IE5 +
  • Chrome toutes les versions
  • Mozilla toutes les versions
  • Yandex toutes les versions

Comment ça marche

Il détecte la fonctionnalité avec le type de stockage.

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

Définit StorageService.localStorage sur window.localStorage s'il est pris en charge ou crée un stockage de cookies . Définit StorageService.sessionStorage sur window.sessionStorage s'il est pris en charge ou crée un stockage en mémoire pour SPA, stockage de cookies avec des fonctionnalités sesion pour non SPA.

1
Ahmet Can Güven

Il semble que Safari 11 modifie le comportement et que le stockage local fonctionne désormais dans une fenêtre de navigateur privée. Hourra!

Notre application Web qui échouait auparavant dans la navigation privée Safari fonctionne désormais parfaitement. Cela fonctionnait toujours très bien dans le mode de navigation privée de Chrome, qui permettait toujours d'écrire sur le stockage local.

Ceci est documenté dans les notes de publication de Safari Technology Preview - et de les notes de publication de WebKit - de Apple pour la version 29, datant de mai 2017. 

Plus précisément:

  • Correction de QuotaExceededError lors de l'enregistrement dans LocalStorage en mode de navigation privée ou dans des sessions WebDriver - r215315
1
karlbecker_com

Ne l'utilisez pas s'il n'est pas pris en charge et pour vérifier le support, appelez simplement cette fonction

partage en Es6 complet en lecture et en écriture localStorage Exemple avec contrôle de support

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

Cela garantira que vos clés sont définies et récupérées correctement sur tous les navigateurs.

0
Tarandeep Singh

Voici une version de service Angular2 + pour une alternative de stockage en mémoire, vous pouvez simplement injecter dans vos composants, sur la base de la réponse de Pierre Le Roux.

import { Injectable } from '@angular/core';

// Alternative to localstorage, memory
// storage for certain browsers in private mode
export class LocalStorageAlternative {
    private  structureLocalStorage = {};

    setItem(key: string, value: string): void {
        this.structureLocalStorage[key] = value;
    }

    getItem(key: string): string {
        if (typeof this.structureLocalStorage[key] !== 'undefined' ) {
            return this.structureLocalStorage[key];
        }
        return null;
    }

    removeItem(key: string): void {
        this.structureLocalStorage[key] = undefined;
    }
}

@Injectable()
export class StorageService {
    private storageEngine;

    constructor() {
        try {
            localStorage.setItem('storage_test', '');
            localStorage.removeItem('storage_test');
            this.storageEngine = localStorage;
        } catch (err) {
            this.storageEngine = new LocalStorageAlternative();
        }
    }

    setItem(key: string, value: string): void {
        this.storageEngine.setItem(key, value);
    }

    getItem(key: string): string {
        return this.storageEngine.getItem(key);
    }

    removeItem(key: string): void {
        this.storageEngine.removeItem(key);
    }

}
0
Gabriel Alack
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }
0
Naim DOGAN