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

时间:2013-01-28 04:16:31

标签: javascript html5 web-applications local-storage

我的webapp在ios safari私密浏览中有javascript错误:

  

JavaScript的:错误

     

未定义

     

QUOTA_EXCEEDED_ERR:DOM例外22:尝试向存储中添加内容......

我的代码:

localStorage.setItem('test',1)

15 个答案:

答案 0 :(得分:183)

显然这是设计上的。当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."

窗口对象仍会在全局命名空间中公开localStorage,但是当您调用setItem时,会抛出此异常。任何对removeItem的调用都会被忽略。

我认为最简单的修复(虽然我还没有测试过这个交叉浏览器)是改变函数isLocalStorageNameSupported()来测试你也可以设置一些值。

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

答案 1 :(得分:37)

上面链接上发布的修复程序对我不起作用。这样做了:

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

派生自http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5

答案 2 :(得分:24)

如其他答案中所述,当调用localStorage.setItem(或sessionStorage.setItem)时,您将始终在iOS和OS X上的Safari私有浏览器模式中获得QuotaExceededError。

一种解决方案是在使用setItem的每个实例中执行try / catch或Modernizr check

但是,如果你想要一个简单地全局停止抛出此错误的垫片,为了防止其他JavaScript被破坏,你可以使用它:

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

答案 3 :(得分:11)

在我的上下文中,刚开发了一个类抽象。 当我的应用程序启动时,我通过调用 getStorage()来检查localStorage是否正常工作。此功能也会返回:

  • 如果localStorage正在运行,则为localStorage
  • 或自定义类 LocalStorageAlternative
  • 的实现

在我的代码中,我从不直接调用localStorage。我打电话给 cusSto 全局变量,我通过调用 getStorage()进行初始化。

这样,它适用于私人浏览或特定的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();

答案 4 :(得分:4)

为了扩展其他人的答案,这是一个紧凑的解决方案,不会公开/添加任何新变量。它并不涵盖所有基础,但它应该适合大多数只希望单页应用程序保持正常运行的人(尽管重新加载后没有数据持久性)。

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

答案 5 :(得分:3)

使用Ionic框架(Angular + Cordova)时遇到了同样的问题。我知道这不能解决问题,但它是基于上述答案的Angular Apps的代码。在iOS版Safari上,您将获得localStorage的短暂解决方案。

以下是代码:

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

来源:https://gist.github.com/jorgecasar/61fda6590dc2bb17e871

享受您的编码!

答案 6 :(得分:2)

以下是AngularJS的解决方案,使用IIFE并利用services are singletons的事实。

这会导致在首次注入服务时立即设置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');
      }
    };
  }
]);

答案 7 :(得分:2)

似乎Safari 11改变了行为,现在本地存储可在专用浏览器窗口中工作。哇!

我们曾经在Safari私人浏览中失败的Web应用程序现在可以完美运行。在Chrome的私有浏览模式下,它始终可以正常运行,该模式始终允许写入本地存储。

这已记录在Apple的Safari Technology Preview release notes-和the WebKit release notes-版本29中,该版本于2017年5月发布。

特别是:

  • 在私人浏览模式或WebDriver会话中保存到localStorage时修复了QuotaExceededError-r215315

答案 8 :(得分:1)

我刚刚创建了此repo,为不受支持或禁用的浏览器提供了sessionStoragelocalStorage功能。

支持的浏览器

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

工作原理

它会检测具有存储类型的功能。

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

如果支持StorageService.localStorage,则将window.localStorage设置为StorageService.sessionStorage或创建Cookie存储。 如果支持,则将window.sessionStorage设置为<div class="form-group"> <label >Category Type</label> <ng-select [options]="category" [(ngModel)]="category.name" name="category" class='filterDropDown' placeholder="Category" notFoundMsg="No Category Found"> </ng-select> </div> ,或者为SPA创建内存存储,使用非SPA的sesion功能存储cookie存储。

答案 9 :(得分:0)

如果不支持则不要使用它,并检查支持只需调用此函数

  

在Es6中完全读取和写入带有支持检查的localStorage示例

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

这将确保在所有浏览器上正确设置和检索您的密钥。

答案 10 :(得分:0)

这里是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);
    }

}

答案 11 :(得分:0)

我已经为该问题创建了补丁。我只是在检查浏览器是否支持localStorage或sessionStorage。如果不是,则存储引擎将为Cookie。但不利的一面是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')

答案 12 :(得分:0)

在几种情况下,可接受的答案似乎并不足够。

要检查是否支持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);
    }
}

像这样使用此代码段,然后回退到例如使用cookie:

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

我制作了fallbackstorage软件包,使用该代码段来检查存储空间的可用性并回退到手动实现的MemoryStorage。

import {getSafeStorage} from 'fallbackstorage'

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

答案 13 :(得分:-1)

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

答案 14 :(得分:-2)

以下脚本解决了我的问题:

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

它检查localStorage是否存在并且可以使用,在否定的情况下,它会创建一个假的本地存储并使用它而不是原始的localStorage。 如果您需要更多信息,请与我们联系。