我的webapp在ios Safari私人浏览中出现javascript错误:
JavaScript:错误
未定义
QUOTA_EXCEEDED_ERR:DOM异常22:试图向存储中添加内容...
我的代码:
localStorage.setItem('test',1)
我的webapp在ios Safari私人浏览中出现javascript错误:
JavaScript:错误
未定义
QUOTA_EXCEEDED_ERR:DOM异常22:试图向存储中添加内容...
我的代码:
localStorage.setItem('test',1)
Answers:
显然,这是设计使然。当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;
}
}
return localStorageName in win && win[localStorageName];
为return true
。然后,您将具有一个函数,该函数根据localStorage的可用性安全地返回true或false。例如:if (isLocalStorageNameSupported()) { /* You can use localStorage.setItem */ } else { /* you can't use localStorage.setItem */ }
上面链接上发布的修复对我不起作用。这样做:
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
window.sessionStorage
是正确的。它当然可以在我的代码中工作。请实际指出您似乎知道的问题的解决方法。
isLocalStorageNameSupported
并正在检查window.sessionStorage
。最终结果相同,但有点混乱。答案已被编辑以澄清。
如其他答案中所述,在调用localStorage.setItem
(或sessionStorage.setItem
)时,在iOS和OS X上的Safari私有浏览器模式下,您始终会收到QuotaExceededError 。
一种解决方案是在使用的每个实例中进行try / catch或Modernizr检查setItem
。
但是,如果您希望使用仅在全局范围内阻止抛出此错误的填充程序,以防止其他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.');
}
}
在我的上下文中,仅开发了一个类抽象。启动应用程序时,我会通过调用getStorage()来检查localStorage是否正常工作。此函数还返回:
在我的代码中,我从不直接调用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();
似乎Safari 11改变了行为,现在本地存储可在专用浏览器窗口中工作。万岁!
我们过去无法在Safari私人浏览中失败的Web应用程序现在可以完美运行。在Chrome的专用浏览模式下,它始终可以正常运行,该模式始终允许写入本地存储。
这在Apple的Safari技术预览版发行说明和WebKit发行说明中都有记录。 -于2017年5月发布的第29版中对此进行了记录。
特别:
为了扩展其他人的答案,这是一个紧凑的解决方案,它不公开/添加任何新变量。它并没有涵盖所有基础,但是它应该适合大多数只希望单个页面应用程序保持功能的人(尽管重新加载后没有数据持久性)。
(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 = {};
};
}
})();
使用离子框架(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
享受您的编码!
这是一个使用IIFE并利用服务为单例的 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');
}
};
}
]);
我刚刚创建的这个回购协议,以提供sessionStorage
和localStorage
功能不支持的或禁用浏览器。
支持的浏览器
这个怎么运作
它使用存储类型检测功能。
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
是否受支持或创建cookie存储。设置StorageService.sessionStorage
为window.sessionStorage
是否支持SPA或在SPA中创建内存存储,为非SPA创建具有会话功能的cookie存储。
这是用于内存存储替代的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);
}
}
在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);
这将确保您的密钥在所有浏览器上均已正确设置和检索。
我已经为该问题创建了一个补丁。我只是在检查浏览器是否支持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')
在几种情况下,可接受的答案似乎不足。
要检查localStorage
or sessionStorage
是否受支持,我使用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
var mod = 'test';
try {
sessionStorage.setItem(mod, mod);
sessionStorage.removeItem(mod);
return true;
} catch (e) {
return false;
}
以下脚本解决了我的问题:
// 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。如果您需要更多信息,请告诉我。