简体   繁体   English

Safari 的 html5 localStorage 错误:“QUOTA_EXCEEDED_ERR:DOM 异常 22:尝试向存储添加超出配额的内容。”

[英]html5 localStorage error with Safari: “QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota.”

My webapp have javascript errors in ios safari private browsing:我的 web 应用程序在 ios safari 隐私浏览中有 javascript 错误:

JavaScript:error JavaScript:错误

undefined不明确的

QUOTA_EXCEEDED_ERR:DOM Exception 22:An attempt was made to add something to storage... QUOTA_EXCEEDED_ERR:DOM 异常 22:尝试向存储中添加内容...

my code:我的代码:

localStorage.setItem('test',1)

Apparently this is by design.显然这是设计使然。 When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage is available, but trying to call setItem throws an exception.当 Safari(OS X 或 iOS)处于隐私浏览模式时,看起来似乎localStorage可用,但尝试调用setItem会引发异常。

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

What happens is that the window object still exposes localStorage in the global namespace, but when you call setItem , this exception is thrown.发生的情况是 window 对象仍然在全局命名空间中公开localStorage ,但是当您调用setItem ,会抛出此异常。 Any calls to removeItem are ignored.removeItem任何调用都将被忽略。

I believe the simplest fix (although I haven't tested this cross browser yet) would be to alter the function isLocalStorageNameSupported() to test that you can also set some value.我相信最简单的修复(虽然我还没有测试过这个跨浏览器)是改变函数isLocalStorageNameSupported()来测试你也可以设置一些值。

https://github.com/marcuswestin/store.js/issues/42 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;
    }
}

The fix posted on above link did not work for me.上面链接上发布的修复程序对我不起作用。 This did:这做到了:

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

Derived from http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5源自http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5

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.正如其他答案中提到的,当localStorage.setItem (或sessionStorage.setItem )被调用时,在 iOS 和 OS X 上的 Safari Private Browser Mode 中,你总是会得到 QuotaExceededError。

One solution is to do a try/catch or Modernizr check in each instance of using setItem .一种解决方案是在使用setItem每个实例中进行 try/catch 或Modernizr 检查

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:但是,如果您想要一个简单地全局停止抛出此错误的 shim,以防止其余 JavaScript 中断,您可以使用以下命令:

https://gist.github.com/philfreo/68ea3cd980d72383c951 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.');
    }
}

In my context, just developed a class abstraction.在我的上下文中,刚刚开发了一个类抽象。 When my application is launched, i check if localStorage is working by calling getStorage() .当我的应用程序启动时,我通过调用getStorage()检查 localStorage 是否正常工作。 This function also return :此函数还返回:

  • either localStorage if localStorage is working如果 localStorage 正在工作,则为 localStorage
  • or an implementation of a custom class LocalStorageAlternative或自定义类LocalStorageAlternative 的实现

In my code i never call localStorage directly.在我的代码中,我从不直接调用 localStorage。 I call cusSto global var, i had initialised by calling getStorage() .我调用cusSto全局变量,我已经通过调用getStorage() 进行了初始化。

This way, it works with private browsing or specific Safari versions这样,它适用于隐私浏览或特定的 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();

It seems that Safari 11 changes the behavior, and now local storage works in a private browser window. Safari 11 似乎改变了行为,现在本地存储在私人浏览器窗口中工作。 Hooray!万岁!

Our web app that used to fail in Safari private browsing now works flawlessly.我们曾经在 Safari 隐私浏览中失败的 Web 应用程序现在可以完美运行。 It always worked fine in Chrome's private browsing mode, which has always allowed writing to local storage.它在 Chrome 的隐私浏览模式下始终运行良好,该模式始终允许写入本地存储。

This is documented in Apple's Safari Technology Preview release notes - and the WebKit release notes - for release 29, which was in May 2017.这记录在 Apple 的Safari Technology Preview 发行说明WebKit 发行说明中,适用于 2017 年 5 月发布的第 29 版。

Specifically:具体来说:

  • Fixed QuotaExceededError when saving to localStorage in private browsing mode or WebDriver sessions - r215315修复了在隐私浏览模式或 WebDriver 会话中保存到 localStorage 时的 QuotaExceededError - r215315

To expand on others' answers, here is a compact solution that doesn't expose/add any new variables.为了扩展其他人的答案,这里有一个不公开/添加任何新变量的紧凑解决方案。 It doesn't cover all bases, but it should suit most people who just want a single page app to remain functional (despite no data persistence after reload).它没有涵盖所有基础,但它应该适合大多数只希望单页应用程序保持功能的人(尽管重新加载后没有数据持久性)。

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

I had the same problem using Ionic framework (Angular + Cordova).我在使用 Ionic 框架(Angular + Cordova)时遇到了同样的问题。 I know this not solve the problem, but it's the code for Angular Apps based on the answers above.我知道这不能解决问题,但它是基于上述答案的 Angular Apps 代码。 You will have a ephemeral solution for localStorage on iOS version of Safari.您将在 iOS 版本的 Safari 上获得 localStorage 的临时解决方案。

Here is the 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来源: https : //gist.github.com/jorgecasar/61fda6590dc2bb17e871

Enjoy your coding!享受你的编码!

Here's a solution for AngularJS using an IIFE and leveraging the fact that services are singletons .这是使用IIFE并利用服务是单例的事实的 AngularJS 解决方案。

This results in isLocalStorageAvailable being set immediately when the service is first injected and avoids needlessly running the check every time local storage needs to be accessed.这导致在首次注入服务时立即设置isLocalStorageAvailable并避免每次需要访问本地存储时不必要地运行检查。

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

I just created this repo to provide sessionStorage and localStorage features for unsupported or disabled browsers.我刚刚创建了这个repo ,为不受支持或禁用的浏览器提供sessionStoragelocalStorage功能。

Supported browsers支持的浏览器

  • IE5+ IE5+
  • Chrome all versions Chrome 所有版本
  • Mozilla all versions Mozilla 所有版本
  • Yandex all versions Yandex所有版本

How it works这个怎么运作

It detects the feature with the storage type.它检测具有存储类型的功能。

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

Sets StorageService.localStorage to window.localStorage if it is supported or creates a cookie storage.如果支持或创建 cookie 存储,则将StorageService.localStorage设置为window.localStorage Sets StorageService.sessionStorage to window.sessionStorage if it is supported or creates a in memory storage for SPA, cookie storage with sesion features for non SPA.如果支持,则将StorageService.sessionStorage设置为window.sessionStorage ,或者为 SPA 创建内存存储,为非 SPA 创建具有会话功能的 cookie 存储。

Here is an Angular2+ service version for memory storage alternative, you can just inject into your components, based on Pierre Le Roux' answer.这是用于内存存储替代方案的 Angular2+ 服务版本,您可以根据 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);
    }

}

The accepted answer seems not adequate in several situations.在几种情况下,公认的答案似乎不够。

To check whether the localStorage or sessionStorage are supported, I use the following snippet from MDN .要检查是否支持localStoragesessionStorage ,我使用MDN 中的以下代码段。

function storageAvailable(type) {
    var storage;
    try {
        storage = window[type];
        var x = '__storage_test__';
        storage.setItem(x, x);
        storage.removeItem(x);
        return true;
    }
    catch(e) {
        return e instanceof DOMException && (
            // everything except Firefox
            e.code === 22 ||
            // Firefox
            e.code === 1014 ||
            // test name field too, because code might not be present
            // everything except Firefox
            e.name === 'QuotaExceededError' ||
            // Firefox
            e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
            // acknowledge QuotaExceededError only if there's something already stored
            (storage && storage.length !== 0);
    }
}

Use this snippet like this, and fallback to, for example, using cookie:像这样使用这个片段,并回退到,例如,使用 cookie:

if (storageAvailable('localStorage')) {
  // Yippee! We can use localStorage awesomeness
}
else {
  // Too bad, no localStorage for us
  document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}

I have made the fallbackstorage package which uses this snippet to check for the storage availability and fallback to a manually implemented MemoryStorage.我制作了fallbackstorage包,它使用这个片段来检查存储可用性并回退到手动实现的 MemoryStorage。

import {getSafeStorage} from 'fallbackstorage'

getSafeStorage().setItem('test', '1') // always work

Don't use it if not supported and to check support just call this function如果不支持,请不要使用它并检查支持只需调用此函数

sharing in Es6 full read and write localStorage Example with support check在 Es6 中共享完全读写 localStorage Example 与支持检查

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

This will make sure your keys are set and retrieved properly on all browsers.这将确保在所有浏览器上正确设置和检索您的密钥。

I have created a patch for the issue.我已经为这个问题创建了一个补丁。 Simply I am checking if the browser does support localStorage or sessionStorage or not.只是我正在检查浏览器是否支持 localStorage 或 sessionStorage 。 If not then the storage engine will be Cookie.如果不是,那么存储引擎将是 Cookie。 But the negative side is Cookie have very tiny storage memory :(但不利的一面是 Cookie 的存储空间非常小:(

function StorageEngine(engine) {
    this.engine = engine || 'localStorage';

    if(!this.checkStorageApi(this.engine)) {
        // Default engine would be alway cooke
        // Safari private browsing issue with localStorage / sessionStorage
        this.engine = 'cookie';
    }
}

StorageEngine.prototype.checkStorageApi = function(name) {
    if(!window[name]) return false;
    try {
        var tempKey = '__temp_'+Date.now();
        window[name].setItem(tempKey, 'hi')
        window[name].removeItem(tempKey);
        return true;
    } catch(e) {
        return false;
    }
}

StorageEngine.prototype.getItem = function(key) {
    if(['sessionStorage', 'localStorage'].includes(this.engine)) {
        return window[this.engine].getItem(key);
    } else if('cookie') {
        var name = key+"=";
        var allCookie = decodeURIComponent(document.cookie).split(';');
        var cval = [];
        for(var i=0; i < allCookie.length; i++) {
            if (allCookie[i].trim().indexOf(name) == 0) {
                cval = allCookie[i].trim().split("=");
            }   
        }
        return (cval.length > 0) ? cval[1] : null;
    }
    return null;
}

StorageEngine.prototype.setItem = function(key, val, exdays) {
    if(['sessionStorage', 'localStorage'].includes(this.engine)) {
        window[this.engine].setItem(key, val);
    } else if('cookie') {
        var d = new Date();
        var exdays = exdays || 1;
        d.setTime(d.getTime() + (exdays*24*36E5));
        var expires = "expires="+ d.toUTCString();
        document.cookie = key + "=" + val + ";" + expires + ";path=/";
    }
    return true;
}


// ------------------------
var StorageEngine = new StorageEngine(); // new StorageEngine('localStorage');
// If your current browser (IOS safary or any) does not support localStorage/sessionStorage, then the default engine will be "cookie"

StorageEngine.setItem('keyName', 'val')

var expireDay = 1; // for cookie only
StorageEngine.setItem('keyName', 'val', expireDay)
StorageEngine.getItem('keyName')
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }

The following script solved my problem:以下脚本解决了我的问题:

// Fake localStorage implementation. 
// Mimics localStorage, including events. 
// It will work just like localStorage, except for the persistant storage part. 

var fakeLocalStorage = function() {
  var fakeLocalStorage = {};
  var storage; 

  // If Storage exists we modify it to write to our fakeLocalStorage object instead. 
  // If Storage does not exist we create an empty object. 
  if (window.Storage && window.localStorage) {
    storage = window.Storage.prototype; 
  } else {
    // We don't bother implementing a fake Storage object
    window.localStorage = {}; 
    storage = window.localStorage; 
  }

  // For older IE
  if (!window.location.origin) {
    window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
  }

  var dispatchStorageEvent = function(key, newValue) {
    var oldValue = (key == null) ? null : storage.getItem(key); // `==` to match both null and undefined
    var url = location.href.substr(location.origin.length);
    var storageEvent = document.createEvent('StorageEvent'); // For IE, http://stackoverflow.com/a/25514935/1214183

    storageEvent.initStorageEvent('storage', false, false, key, oldValue, newValue, url, null);
    window.dispatchEvent(storageEvent);
  };

  storage.key = function(i) {
    var key = Object.keys(fakeLocalStorage)[i];
    return typeof key === 'string' ? key : null;
  };

  storage.getItem = function(key) {
    return typeof fakeLocalStorage[key] === 'string' ? fakeLocalStorage[key] : null;
  };

  storage.setItem = function(key, value) {
    dispatchStorageEvent(key, value);
    fakeLocalStorage[key] = String(value);
  };

  storage.removeItem = function(key) {
    dispatchStorageEvent(key, null);
    delete fakeLocalStorage[key];
  };

  storage.clear = function() {
    dispatchStorageEvent(null, null);
    fakeLocalStorage = {};
  };
};

// Example of how to use it
if (typeof window.localStorage === 'object') {
  // Safari will throw a fit if we try to use localStorage.setItem in private browsing mode. 
  try {
    localStorage.setItem('localStorageTest', 1);
    localStorage.removeItem('localStorageTest');
  } catch (e) {
    fakeLocalStorage();
  }
} else {
  // Use fake localStorage for any browser that does not support it.
  fakeLocalStorage();
}

It checks if localStorage exists and can be used and in the negative case, it creates a fake local storage and uses it instead of the original localStorage.它会检查 localStorage 是否存在并且可以使用,在否定的情况下,它会创建一个假的本地存储并使用它而不是原始的 localStorage。 Please let me know if you need further information.如果您需要更多信息,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 QuotaExceededError:Dom异常22:尝试向存储中添加超出配额的内容 - QuotaExceededError: Dom exception 22: An attempt was made to add something to storage that exceeded the quota 有没有办法增加谷歌浏览器中 localStorage 的大小以避免 QUOTA_EXCEEDED_ERR: DOM Exception 22 - Is there a way to increase the size of localStorage in Google Chrome to avoid QUOTA_EXCEEDED_ERR: DOM Exception 22 在localStorage上捕获QUOTA_EXCEEDED_ERR - catch QUOTA_EXCEEDED_ERR on localStorage 防止localStorage QUOTA_EXCEEDED_ERR - Preventing localStorage QUOTA_EXCEEDED_ERR 在iOS上超过LocalStorage配额(dom错误22),但未设置私有模式 - LocalStorage quota exceeded (dom error 22) on iOS, but private mode not set QuotaExceededError(DOM 异常 22):在 Safari 隐身模式下已超出配额 - QuotaExceededError (DOM Exception 22): The quota has been exceeded on Safari in incognito Chrome 文件系统 API 中的 QUOTA_EXCEEDED_ERR - QUOTA_EXCEEDED_ERR in Chrome FileSystem API 为什么收到 Firebase Quota 超出错误? - WHy getting Firebase Quota exceeded error? 任何用于模拟低存储空间的浏览器开发工具(超出配额) - Any browser dev tools to simulate low storage (quota exceeded) 未捕获(承诺)DOMException:超出配额 - Uncaught (in promise) DOMException: Quota exceeded
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM