ChatGPT解决这个技术问题 Extra ChatGPT

QuotaExceededError: Dom exception 22: An attempt was made to add something to storage that exceeded the quota

Using LocalStorage on iPhone with iOS 7 throws this error. I've been looking around for a resolvant, but considering I'm not even browsing in private, nothing is relevant.

I don't understand why localStorage would be disabled by default in iOS 7, but it seems it is? I've tested on other websites as well, but with no luck. I even tried testing it using this website: http://arty.name/localstorage.html, but it doesn't seem like it's saving anything at all for some weird reason.

Has anyone had the same problem, only they've had luck fixing it? Should I switch my storage method?

I tried hard-debugging it by only storing a few lines of information, but to no avail. I used the standard localStorage.setItem() function to save.

It usually means you tried to store something with a size that exceeded available storage space. What browser are you using (Safari, Chrome, etc.)? Can you share a little more of the code you have been using and if possible the data you're trying to store.
This should be considered as a bug or issue on the Safari side. It does not make sense that you can not use localStorage in incognito mode...
Use a feature detect that tests for this specific issue. If storage is not available, consider shimming localStorage with memoryStorage. disclaimer: I am the author of the linked packages
In April 2017 a patch was merged into Safari, so it aligned with the other browsers. Will likely land in Safari 11. bugs.webkit.org/show_bug.cgi?id=157010
I can confirm this has been fixed in Safari iOS 11. Tested Private browsing + sessionStorage.setItem() then sessionStorage.getItem() successfully on iPhone6 and iPhone8.

s
sandstrom

This can occur when Safari is in private mode browsing. While in private browsing, local storage is not available at all.

One solution is to warn the user that the app needs non-private mode to work.

UPDATE: This has been fixed in Safari 11, so the behaviour is now aligned with other browsers.


Your post was just incredibly helpful and timely for me today (less than 24 hours later). For reference, here's how to turn on/off private browsing: imore.com/how-use-private-browsing-ios-7-safari
+1 fixed my issue. I was checking for the existence of LocalStorage (if( typeof Storage != 'undefined' ) { ... }) before trying to load and save information but getting this error. Turns out Storage is still defined even when it's unusable. Using try/catch from now on whenever I use LocalStorage.
Thanks! Weird error by safari. Should have been more informative. :D
A fix may be incoming as of Safari Tech Preview 29: "Fixed QuotaExceededError when saving to localStorage in private browsing mode or WebDriver sessions". See developer.apple.com/safari/technology-preview/release-notes
This can also occur if storage limit is reached which can be easily done by saving images for example.
p
philfreo

As mentioned in other answers, you'll always get the QuotaExceededError in Safari Private Browser Mode on both iOS and OS X when localStorage.setItem (or sessionStorage.setItem) is called.

One solution is to do a try/catch or Modernizr check in each instance of using setItem.

However if you want a shim that simply globally stops this error being thrown, to prevent the rest of your JavaScript from breaking, you can use this:

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

Why add the setItem to the Storage object if you wont be able to use it anyway?
The point of my snippet is to simply ignore JS errors from being thrown if you want your app to not be totally broken in Safari private mode.
D
DrewT

I use this simple function, which returns true or false, to test for localStorage availablity:

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

Now you can test for localStorage.setItem() availability before using it. Example:

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

Have I missed something? Why is window.sessionStorage been used instead of window.localStorage for a method called isLocalStorageNameSupported?
@lthar - see the documentation here: w3schools.com/html/html5_webstorage.asp Most importantly this part: 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, but what's the difference in this situation if you remove your test key? It doesn't matter where I will store my test key if I am going to delete it. Am I wrong? Why is the session storage better that the local one?
@TurakVladyslav you're right there is really no difference here except that using sessionStorage makes it more manageable for setting breakpoints if you want to test your development. There is no true argument for which is "better" and it's really just a personal preference here that errs on the side of caution. The main thing to note is that both sessionStorage and localStorage are both implementations of the HTML5 webstorage API.
d
defvol

I happened to run with the same issue in iOS 7 (with some devices no simulators).

Looks like Safari in iOS 7 has a lower storage quota, which apparently is reached by having a long history log.

I guess the best practice will be to catch the exception.

The Modernizr project has an easy patch, you should try something similar: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js


S
Stickley

Here is an expanded solution based on DrewT's answer above that uses cookies if localStorage is not available. It uses Mozilla's docCookies library:

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

In your source, just use:

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

J
Josef Engelfrost

As already explained in other answers, when in Private Browsing mode Safari will always throw this exception when trying to save data with localStorage.setItem() .

To fix this I wrote a fake localStorage that mimics localStorage, both methods and events.

Fake localStorage: https://gist.github.com/engelfrost/fd707819658f72b42f55

This is probably not a good general solution to the problem. This was a good solution for my scenario, where the alternative would be major re-writes to an already existing application.


What exactly does it fix? It doesn't persist anything, so what's the point?
It "fixes" Safari when in private browsing mode. (This is not clear in my answer, thanks for pointing that out. I'll edit my answer). Nothing is supposed to be persisted when in private browsing mode regardless, so not persisting is not a relevant issue here. What this fixed for me was to allow users to run an already existing application, without major re-writes, even when in Private Browsing mode in Safari.
J
John

Update (2016-11-01)

I was using AmplifyJS mentioned below to work around this issue. However, for Safari in Private browsing, it was falling back to a memory-based storage. In my case, it was not appropriate because it means the storage is cleared on refresh, even if the user is still in private browsing.

Also, I have noticed a number of users who are always browsing in Private mode on iOS Safari. For that reason, a better fallback for Safari is to use cookies (if available). By default, cookies are still accessible even in private browsing. Of course, they are cleared when exiting the private browsing, but they are not cleared on refresh.

I found the local-storage-fallback library. From the documentation:

Purpose With browser settings like "Private Browsing" it has become a problem to rely on a working window.localStorage, even in newer browsers. Even though it may exist, it will throw exceptions when trying to use setItem or getItem. This module will run appropriate checks to see what browser storage mechanism might be available, and then expose it. It uses the same API as localStorage so it should work as a drop-in replacement in most cases.

Beware of the gotchas:

CookieStorage has storage limits. Be careful here. MemoryStorage will not persist between page loads. This is more or less a stop-gap to prevent page crashes, but may be sufficient for websites that don't do full page loads.

TL;DR:

Use local-storage-fallback (unified API with .getItem(prop) and .setItem(prop, val)):

Check and use appropriate storage adapter for browser (localStorage, sessionStorage, cookies, memory)

Original answer

To add upon previous answers, one possible workaround would be to change the storage method. There are a few librairies such as AmplifyJS and PersistJS which can help. Both libs allow persistent client-side storage through several backends.

For AmplifyJS

localStorage 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+ userData IE 5 - 7 userData exists in newer versions of IE as well, but due to quirks in IE 9's implementation, we don't register userData if localStorage is supported. memory An in-memory store is provided as a fallback if none of the other storage types are available.

For PersistentJS

flash: Flash 8 persistent storage. gears: Google Gears-based persistent storage. localstorage: HTML5 draft storage. globalstorage: HTML5 draft storage (old spec). ie: Internet Explorer userdata behaviors. cookie: Cookie-based persistent storage.

They offer an abstraction layer so you don't have to worry about choosing the storage type. Keep in mind there might be some limitations (such as size limits) depending on the storage type though. Right now, I am using AmplifyJS, but I still have to do some more testing on iOS 7/Safari/etc. to see if it actually solves the problem.


Editor John: I realize you and Jonathan Alzetta are probably the same account and you're just trying to improve your answer, but if so you should really log-in as Jonathan Alzetta and edit this answer, and then it won't go through the review queue. Recover your account if you need to.
s
sandstrom

In April 2017 a patch was merged into Safari, so it aligned with the other browsers. This was released with Safari 11.

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


c
clayostrom

This question and answer helped me solve a specific problem with signing up new users in Parse.

Because the signUp( attrs, options ) function uses local storage to persist the session, if a user is in private browsing mode it throws the "QuotaExceededError: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota." exception and the success/error functions are never called.

In my case, because the error function is never called it initially appeared to be an issue with firing the click event on the submit or the redirect defined on success of sign up.

Including a warning for users resolved the issue.

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

Signs up a new user with a username (or email) and password. This will create a new Parse.User on the server, and also persist the session in localStorage so that you can access the user using {@link #current}.