Moja aplikacja internetowa ma błędy javascript podczas przeglądania prywatnego safari na iOS:
JavaScript: błąd
nieokreślony
QUOTA_EXCEEDED_ERR: DOM Wyjątek 22: Podjęto próbę dodania czegoś do magazynu ...
mój kod:
localStorage.setItem('test',1)
Moja aplikacja internetowa ma błędy javascript podczas przeglądania prywatnego safari na iOS:
JavaScript: błąd
nieokreślony
QUOTA_EXCEEDED_ERR: DOM Wyjątek 22: Podjęto próbę dodania czegoś do magazynu ...
mój kod:
localStorage.setItem('test',1)
Odpowiedzi:
Najwyraźniej jest to zamierzone. Gdy Safari (OS X lub iOS) jest w trybie przeglądania prywatnego, wygląda na localStorageto, że jest dostępne, ale próba wywołania setItemzgłasza wyjątek.
store.js line 73
"QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to add something to storage that exceeded the quota."
Dzieje się tak, że obiekt okna nadal uwidacznia się localStoragew globalnej przestrzeni nazw, ale podczas wywołania setItemzgłaszany jest ten wyjątek. Wszelkie wezwania do removeItemsą ignorowane.
Uważam, że najprostszą poprawką (chociaż nie testowałem jeszcze tej przeglądarki) byłaby zmiana funkcji, isLocalStorageNameSupported()aby sprawdzić, czy można również ustawić jakąś wartość.
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];na return true. Następnie masz funkcję, która bezpiecznie zwraca wartość true lub false w zależności od dostępności localStorage. Na przykład:if (isLocalStorageNameSupported()) { /* You can use localStorage.setItem */ } else { /* you can't use localStorage.setItem */ }
Poprawka zamieszczona pod powyższym linkiem nie działa dla mnie. To zrobiło:
function isLocalStorageNameSupported() {
var testKey = 'test', storage = window.localStorage;
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
}
Pochodzi z http://m.cg/post/13095478393/detect-private-browsing-mode-in-mobile-safari-on-ios5
window.sessionStorageto prawda. Z pewnością działa w moim kodzie. Wskaż sposób rozwiązania problemu, o którym wiesz.
isLocalStorageNameSupportedi sprawdzałem window.sessionStorage. Ten sam efekt końcowy, ale był trochę zagmatwany. Odpowiedź została zredagowana w celu wyjaśnienia.
Jak wspomniano w innych odpowiedziach, zawsze otrzymasz błąd QuotaExceededError w trybie przeglądarki prywatnej Safari w systemie iOS i OS X, gdy zostanie wywołany localStorage.setItem(lub sessionStorage.setItem).
Jednym z rozwiązań jest wykonanie testu try / catch lub Modernizr w każdym przypadku użycia setItem.
Jeśli jednak potrzebujesz podkładki, która po prostu globalnie zapobiega zgłaszaniu tego błędu, aby zapobiec uszkodzeniu reszty JavaScript, możesz użyć tego:
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.');
}
}
W moim kontekście właśnie opracowałem abstrakcję klas. Po uruchomieniu aplikacji sprawdzam, czy działa localStorage, wywołując metodę getStorage () . Ta funkcja zwraca również:
W moim kodzie nigdy nie dzwonię bezpośrednio do localStorage. Wzywam cusSto globalny var miałem zainicjowane przez wywołanie getStorage () .
W ten sposób działa z przeglądaniem prywatnym lub określonymi wersjami 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();
Wygląda na to, że Safari 11 zmienia zachowanie, a teraz pamięć lokalna działa w prywatnym oknie przeglądarki. Brawo!
Nasza aplikacja internetowa, która wcześniej nie działała podczas przeglądania prywatnego w Safari, teraz działa bez zarzutu. Zawsze działało dobrze w trybie przeglądania prywatnego Chrome, który zawsze pozwalał na zapisywanie w pamięci lokalnej.
Jest to udokumentowane w informacjach o wersji Safari Technology Preview firmy Apple - oraz w informacjach o wydaniu WebKit - dla wersji 29, która miała miejsce w maju 2017 r.
Konkretnie:
Aby rozwinąć odpowiedzi innych osób, oto kompaktowe rozwiązanie, które nie ujawnia / nie dodaje żadnych nowych zmiennych. Nie obejmuje wszystkich baz, ale powinien odpowiadać większości osób, które chcą, aby aplikacja z jedną stroną pozostała funkcjonalna (pomimo braku trwałości danych po ponownym załadowaniu).
(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 = {};
};
}
})();
Miałem ten sam problem używając frameworka Ionic (Angular + Cordova). Wiem, że to nie rozwiązuje problemu, ale jest to kod dla Angular Apps oparty na powyższych odpowiedziach. Będziesz mieć efemeryczne rozwiązanie dla localStorage w wersji Safari na iOS.
Oto kod:
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);
}
}
}
]);
Źródło: https://gist.github.com/jorgecasar/61fda6590dc2bb17e871
Życzymy udanego kodowania!
Oto rozwiązanie dla AngularJS wykorzystujące IIFE i wykorzystujące fakt, że usługi są singletonami .
Powoduje to, isLocalStorageAvailableże jest ustawiana natychmiast po pierwszym wstrzyknięciu usługi i pozwala uniknąć niepotrzebnego przeprowadzania sprawdzania za każdym razem, gdy trzeba uzyskać dostęp do pamięci lokalnej.
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');
}
};
}
]);
Właśnie utworzyłem to repozytorium, aby zapewnić sessionStoragei localStoragefunkcje dla nieobsługiwanych lub wyłączonych przeglądarek.
Obsługiwane przeglądarki
Jak to działa
Wykrywa funkcję z typem pamięci.
function(type) {
var testKey = '__isSupported',
storage = window[type];
try {
storage.setItem(testKey, '1');
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
Zestawy StorageService.localStoragedo window.localStoragejeśli jest obsługiwany lub tworzy przechowywania cookie. Zestawy StorageService.sessionStoragedo window.sessionStoragejeśli jest obsługiwany lub tworzy w pamięci do przechowywania, magazynowania SPA ciasteczka z funkcji Sesión za nieprzestrzeganie SPA.
Oto wersja usługi Angular2 + dla alternatywnego przechowywania pamięci, którą możesz po prostu wstrzyknąć do swoich komponentów, w oparciu o odpowiedź Pierre'a 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);
}
}
udostępnianie w Es6 pełny odczyt i zapis localStorage Przykład z kontrolą wsparcia
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);
Zapewni to prawidłowe ustawienie i pobieranie kluczy we wszystkich przeglądarkach.
Stworzyłem łatkę na ten problem. Po prostu sprawdzam, czy przeglądarka obsługuje localStorage lub sessionStorage, czy nie. Jeśli nie, to mechanizm przechowywania będzie Cookie. Ale negatywną stroną jest to, że Cookie ma bardzo małą pamięć :(
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')
Przyjęta odpowiedź wydaje się nieadekwatna w kilku sytuacjach.
Aby sprawdzić, czy obsługiwane localStoragelub sessionStoragesą obsługiwane, używam następującego fragmentu z 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);
}
}
Użyj tego fragmentu kodu w ten sposób i wróć do, na przykład, pliku cookie:
if (storageAvailable('localStorage')) {
// Yippee! We can use localStorage awesomeness
}
else {
// Too bad, no localStorage for us
document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}
Zrobiłem pakiet fallbackstorage , który używa tego fragmentu kodu do sprawdzenia dostępności magazynu i powrotu do ręcznie zaimplementowanego 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;
}
Poniższy skrypt rozwiązał mój problem:
// 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();
}
Sprawdza, czy localStorage istnieje i może być używany, aw negatywnym przypadku tworzy fałszywą lokalną pamięć masową i używa jej zamiast oryginalnego localStorage. Daj mi znać, jeśli potrzebujesz dodatkowych informacji.