เว็บแอปของฉันมีข้อผิดพลาดจาวาสคริปต์ในการเรียกดูแบบส่วนตัวของ iOS Safari:
JavaScript: ข้อผิดพลาด
ไม่ได้กำหนด
QUOTA_EXCEEDED_ERR: DOM Exception 22: มีความพยายามที่จะเพิ่มบางสิ่งลงในที่เก็บข้อมูล ...
รหัสของฉัน:
localStorage.setItem('test',1)
เว็บแอปของฉันมีข้อผิดพลาดจาวาสคริปต์ในการเรียกดูแบบส่วนตัวของ iOS Safari:
JavaScript: ข้อผิดพลาด
ไม่ได้กำหนด
QUOTA_EXCEEDED_ERR: DOM Exception 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
ข้อยกเว้นนี้จะถูกโยนทิ้ง สายเรียกเข้า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 ตัวอย่าง: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
ผลลัพธ์สุดท้ายเหมือนกัน แต่สับสนเล็กน้อย คำตอบถูกแก้ไขเพื่อชี้แจง
ตามที่กล่าวไว้ในคำตอบอื่น ๆ คุณจะได้รับ QuotaExceededError ใน Safari Private Browser Mode ทั้งบน iOS และ OS X เมื่อมีการเรียกlocalStorage.setItem
(หรือsessionStorage.setItem
)
ทางออกหนึ่งคือการทำลอง / จับหรือตรวจสอบ ModernizrsetItem
ในกรณีของการใช้แต่ละ
อย่างไรก็ตามหากคุณต้องการ 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.');
}
}
ในบริบทของฉันเพิ่งพัฒนาสิ่งที่เป็นนามธรรมของชั้นเรียน เมื่อใบสมัครของฉันมีการเปิดตัวผมตรวจสอบว่า localStorage คือการทำงานโดยการเรียกgetStorage () ฟังก์ชันนี้ยังส่งคืน:
ในรหัสของฉันฉันไม่เคยโทรหา localStorage โดยตรง ผมเรียกcusSto 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();
ดูเหมือนว่า Safari 11 จะเปลี่ยนพฤติกรรมและตอนนี้ที่เก็บข้อมูลในเครื่องทำงานในหน้าต่างเบราว์เซอร์ส่วนตัว ไชโย!
เว็บแอปของเราที่เคยล้มเหลวในการท่องเว็บแบบส่วนตัวของ Safari ตอนนี้ทำงานได้อย่างไม่มีที่ติ มันทำงานได้ดีเสมอในโหมดการท่องเว็บส่วนตัวของ Chrome ซึ่งอนุญาตให้เขียนลงในพื้นที่จัดเก็บข้อมูลในเครื่องได้เสมอ
เอกสารนี้อยู่ในบันทึกประจำรุ่น Safari Technology Previewของ Apple และบันทึกประจำรุ่น WebKitสำหรับรุ่นที่ 29 ซึ่งเป็นในเดือนพฤษภาคม 2017
โดยเฉพาะ:
หากต้องการขยายคำตอบของผู้อื่นนี่คือโซลูชันขนาดกะทัดรัดที่ไม่เปิดเผย / เพิ่มตัวแปรใหม่ ๆ ไม่ครอบคลุมทุกฐาน แต่ควรเหมาะกับคนส่วนใหญ่ที่ต้องการให้แอปหน้าเดียวใช้งานได้ (แม้จะไม่มีข้อมูลคงอยู่หลังจากโหลดซ้ำ)
(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 = {};
};
}
})();
ฉันมีปัญหาเดียวกันกับการใช้ Ionic framework (Angular + Cordova) ฉันรู้ว่าสิ่งนี้ไม่สามารถแก้ปัญหาได้ แต่เป็นรหัสสำหรับ Angular Apps ตามคำตอบด้านบน คุณจะมีวิธีแก้ปัญหาชั่วคราวสำหรับ localStorage บน Safari เวอร์ชัน iOS
นี่คือรหัส:
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
สนุกกับการเขียนโค้ดของคุณ!
นี่คือทางออกสำหรับ AngularJS ใช้IIFEและใช้ประโยชน์จากความจริงที่ว่าบริการ 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');
}
};
}
]);
ฉันเพิ่งสร้างrepoนี้เพื่อให้บริการ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
ถ้ามันได้รับการสนับสนุนหรือสร้างจัดเก็บคุกกี้ ชุดStorageService.sessionStorage
ไปwindow.sessionStorage
ถ้ามันได้รับการสนับสนุนหรือสร้างในการจัดเก็บหน่วยความจำสำหรับสปาที่เก็บคุกกี้ sesion ให้บริการสำหรับผู้ที่ไม่ใช่สปา
นี่คือเวอร์ชันบริการ 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
หรือ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);
}
}
ใช้ตัวอย่างข้อมูลในลักษณะนี้และใช้ทางเลือกอื่นเช่นการใช้คุกกี้:
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 ดั้งเดิม โปรดแจ้งให้เราทราบหากคุณต้องการข้อมูลเพิ่มเติม