Javascript Safari出现html5本地存储错误:";配额“超出”错误:DOM异常22:试图向超出配额的存储中添加内容;

Javascript Safari出现html5本地存储错误:";配额“超出”错误:DOM异常22:试图向超出配额的存储中添加内容;,javascript,html,local-storage,Javascript,Html,Local Storage,我的webapp在ios safari私人浏览中出现javascript错误: JavaScript:错误 未定义 配额\u已超过\u错误:DOM异常22:试图向存储中添加某些内容 我的代码: localStorage.setItem('test',1) 显然这是故意的。当Safari(OS X或iOS)处于私人浏览模式时,似乎localStorage可用,但尝试调用setItem会引发异常 store.js line 73 "QUOTA_EXCEEDED_ERR: DOM Exception

我的webapp在ios safari私人浏览中出现javascript错误:

JavaScript:错误

未定义

配额\u已超过\u错误:DOM异常22:试图向存储中添加某些内容

我的代码:

localStorage.setItem('test',1)

显然这是故意的。当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
时,会引发此异常。对
removietem
的任何调用都将被忽略

我相信最简单的修复方法(尽管我还没有测试这个跨浏览器)是修改函数
isLocalStorageNameSupported()
,以测试您还可以设置一些值


上面链接上发布的补丁对我不起作用。这确实:

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

源于我的上下文中的

,刚刚开发了一个类抽象。 启动应用程序时,我通过调用getStorage()检查本地存储是否正常工作。此函数还返回:

  • 如果localStorage正在工作,则选择localStorage
  • 或自定义类的实现LocalStorageAlternative
在我的代码中,我从不直接调用localStorage。我调用cusStoglobal var,通过调用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();

我在使用离子骨架(Angular+Cordova)时也遇到了同样的问题。我知道这不能解决问题,但这是基于上述答案的Angular应用程序代码。在iOS版本的Safari上,您将有一个本地存储的临时解决方案

代码如下:

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);
            }
        }
    }
]);
资料来源:


享受你的编码

如其他答案中所述,当调用
localStorage.setItem
(或
sessionStorage.setItem
)时,在iOS和OS X上的Safari专用浏览器模式下,您总是会收到QuoteExceedeError

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

但是,如果您希望使用一个垫片来全局停止抛出此错误,以防止其余JavaScript中断,您可以使用以下方法:


这里有一个AngularJS的解决方案,它使用

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

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

(函数(){
试一试{
setItem(“存储测试”、“测试”);
localStorage.removeItem(“存储测试”);
}捕获(exc){
var tmp_storage={};
var p='\uuuuu unique\uuuu';//为所有键添加前缀以避免匹配内置项
Storage.prototype.setItem=函数(k,v){
tmp_存储[p+k]=v;
};
Storage.prototype.getItem=函数(k){
返回tmp_存储器[p+k]==未定义?空:tmp_存储器[p+k];
};
Storage.prototype.removietem=函数(k){
删除tmp_存储器[p+k];
};
Storage.prototype.clear=函数(){
tmp_存储={};
};
}
})();

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

// 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();
}
它检查本地存储是否存在并可以使用,如果不存在,它会创建一个假的本地存储并使用它,而不是原始的本地存储。 如果您需要更多信息,请告诉我。

如果不支持,请不要使用它,要检查支持情况,请调用此函数 使用支持检查在Es6中共享完全读写本地存储示例

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

我创建它只是为了为不受支持或禁用的浏览器提供
sessionStorage
localStorage
功能

支持的浏览器

  • 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;
    }
};
如果支持或创建cookie存储,则将
StorageService.localStorage
设置为
window.localStorage

StorageService.sessionStorage
设置为
窗口。sessionStorage
如果受支持或为SPA创建内存中存储,则为非SPA创建具有sesion功能的cookie存储。

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

我们曾经在Safari私人浏览中失败的web应用现在可以完美地工作。在Chrome的私有浏览模式下,它总是工作得很好,该模式始终允许写入本地存储

这一点在2017年5月苹果发布的第29版中有记录

具体而言:

  • 修复了在私人浏览模式或WebDriver会话中保存到本地存储时出现的QuotaExceedeError-

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

}

我已经为这个问题创建了一个补丁。我只是检查浏览器是否支持本地存储或会话存储。如果不是,则存储引擎将是Cookie。但不利的一面是Cookie的存储内存非常小:(

功能存储引擎(引擎){
这个发动机|
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);
function(type) {
    var testKey = '__isSupported',
        storage = window[type];
    try {
        storage.setItem(testKey, '1');
        storage.removeItem(testKey);
        return true;
    } catch (error) {
        return false;
    }
};
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }
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);
    }

}
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')
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);
    }
}
if (storageAvailable('localStorage')) {
  // Yippee! We can use localStorage awesomeness
}
else {
  // Too bad, no localStorage for us
  document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}
import {getSafeStorage} from 'fallbackstorage'

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