134

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

JavaScript:错误

不明确的

QUOTA_EXCEEDED_ERR:DOM 异常 22:试图向存储中添加某些内容...

我的代码:

localStorage.setItem('test',1)
4

15 回答 15

187

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

发生的情况是 window 对象仍然暴露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;
    }
}
于 2013-01-28T04:18:41.113 回答
38

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

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

于 2013-07-11T22:40:04.957 回答
25

localStorage.setItem正如其他答案中提到的那样,在调用(or sessionStorage.setItem)时,您总是会在 iOS 和 OS X 上的 Safari Private Browser Mode 中获得 QuotaExceededError 。

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

但是,如果您想要一个简单地全局阻止此错误抛出的 shim,以防止 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.');
    }
}
于 2014-11-22T19:17:30.303 回答
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();
于 2013-07-16T15:42:16.537 回答
7

Safari 11 似乎改变了这种行为,现在本地存储在私人浏览器窗口中工作。万岁!

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

这在 2017 年 5 月发布的第 29版Apple 的Safari 技术预览版发行说明WebKit 发行说明中有记录。

具体来说:

  • 修复了在隐私浏览模式或 WebDriver 会话中保存到 localStorage 时出现的 QuotaExceededError - r215315
于 2018-07-31T14:49:58.703 回答
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 = {};
        };
    }
})();
于 2016-02-25T09:33:05.343 回答
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

享受你的编码!

于 2014-08-26T18:51:34.093 回答
2

这是一个使用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');
      }
    };
  }
]);
于 2015-03-20T16:43:48.253 回答
2

在几种情况下,公认的答案似乎不够充分。

要检查localStorageorsessionStorage是否受支持,我使用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
于 2020-07-25T08:59:13.547 回答
1

我刚刚创建了这个repo来为不受支持或禁用的浏览器提供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;
    }
};

设置StorageService.localStoragewindow.localStorage是否支持或创建 cookie 存储。设置StorageService.sessionStoragewindow.sessionStorage是否支持或为 SPA 创建内存存储,为非 SPA 创建具有会话功能的 cookie 存储。

于 2017-11-14T07:20:05.697 回答
1

这是用于内存存储替代方案的 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);
    }

}
于 2019-03-28T20:37:31.607 回答
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);

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

于 2017-08-10T18:41:20.470 回答
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')
于 2019-12-17T20:28:06.853 回答
-1
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }
于 2018-10-04T11:26:50.700 回答
-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。如果您需要更多信息,请告诉我。

于 2016-07-28T12:33:43.690 回答