QuotaExceededError: исключение Dom 22: была предпринята попытка добавить в хранилище что-то, превышающее квоту

219

Использование LocalStorage на iPhone с iOS 7 выдает эту ошибку. Я искал резольвант, но, учитывая, что я даже не заглядываю в приват, ничего не имеет значения.

Я не понимаю, почему localStorage будет отключен по умолчанию в iOS 7, но кажется, что это так? Я тестировал и на других сайтах, но безуспешно. Я даже пытался протестировать его с помощью этого сайта: http://arty.name/localstorage.html , но не похоже, что он вообще что-то сохраняет по какой-то странной причине.

У кого-нибудь была такая же проблема, только им повезло, исправляя ее? Должен ли я изменить свой метод хранения?

Я попытался отладить его, сохранив только несколько строк информации, но безрезультатно. Я использовал стандартную localStorage.setItem()функцию для сохранения.

НИКТ
источник
2
Обычно это означает, что вы пытались сохранить что-то с размером, превышающим доступное место для хранения. Какой браузер вы используете (Safari, Chrome и т. Д.)? Можете ли вы поделиться немного больше кода, который вы использовали, и, если возможно, данные, которые вы пытаетесь сохранить.
3
Это следует рассматривать как ошибку или проблему на стороне Safari. Не имеет смысла, что вы не можете использовать localStorage в режиме инкогнито ...
Максим Лузик
Используйте функцию обнаружения этого теста для этой конкретной проблемы . Если хранилище недоступно, рассмотрите возможность использования localStorage с помощью memoryStorage . Отказ от ответственности: я являюсь автором связанных пакетов
Stijn de Witt
1
В апреле 2017 года патч был объединен с Safari, поэтому он соответствовал другим браузерам. Скорее всего, приземлится в Safari 11. bugs.webkit.org/show_bug.cgi?id=157010
песчаная буря
2
Я могу подтвердить, что это было исправлено в Safari iOS 11. Протестировано Закрытый просмотр + sessionStorage.setItem (), затем sessionStorage.getItem () на iPhone6 ​​и iPhone8.
Кевин

Ответы:

372

Это может произойти, когда Safari находится в режиме приватного просмотра. Во время приватного просмотра локальное хранилище вообще не доступно.

Одним из решений является предупреждение пользователя о том, что для работы приложения необходим не-приватный режим.

ОБНОВЛЕНИЕ: это было исправлено в Safari 11 , поэтому теперь поведение соответствует другим браузерам.

Кристиан Дину
источник
4
Ваш пост был просто невероятно полезным и своевременным для меня сегодня (менее чем через 24 часа). Для справки вот как включить / выключить приватный просмотр: imore.com/how-use-private-browsing-ios-7-safari
Ник
12
+1 исправил мою проблему. Я проверял существование LocalStorage ( if( typeof Storage != 'undefined' ) { ... }), прежде чем пытаться загрузить и сохранить информацию, но получил эту ошибку. Оказывается, Storageвсе еще определяется, даже когда это непригодно. Используя try / catch, теперь я использую LocalStorage.
Стивендесу
Спасибо! Странная ошибка сафари. Должно быть более информативным. : D
Солнечный R Гупта
2
Исправление может поступить с Safari Tech Preview 29: «Исправлена ​​ошибка QuotaExceededError при сохранении в localStorage в режиме частного просмотра или сеансах WebDriver». См developer.apple.com/safari/technology-preview/release-notes
Марк Баумбы
1
Это может также произойти, если достигнут предел хранения, который может быть легко сделан, например, сохранением изображений.
csalmeida
103

Как упоминалось в других ответах, вы всегда будете получать ошибку QuotaExceededError в режиме приватного браузера Safari как на iOS, так и на OS X при вызове localStorage.setItem(или sessionStorage.setItem).

Одним из решений является проверка 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.');
    }
}
philfreo
источник
1
Зачем добавлять setItem в объект Storage, если вы все равно не сможете его использовать?
Некромант
4
Смысл моего фрагмента в том, чтобы просто игнорировать ошибки JS, возникающие при их выдаче, если вы хотите, чтобы ваше приложение не полностью ломалось в приватном режиме Safari.
Филфрео
16

Я использую эту простую функцию, которая возвращает trueили false, для проверки доступности localStorage:

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

Теперь вы можете проверить localStorage.setItem()наличие, прежде чем использовать его. Пример:

if ( isLocalStorageNameSupported() ) {
    // can use localStorage.setItem('item','value')
} else {
    // can't use localStorage.setItem('item','value')
}
DrewT
источник
Я что-то пропустил? Почему window.sessionStorageиспользуется вместо window.localStorageвызываемого метода isLocalStorageNameSupported?
Ithar
@lthar - см. документацию здесь: w3schools.com/html/html5_webstorage.asp Наиболее важная часть:HTML local storage provides two objects for storing data on the client: window.localStorage - stores data with no expiration date window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)
DrewT
@DrewT, но какая разница в этой ситуации, если вы удалите свой тестовый ключ? Неважно, где я буду хранить свой тестовый ключ, если я собираюсь удалить его. Я ошибся? Почему хранилище сессии лучше локального?
Владислав Турак
1
@TurakVladyslav Вы правы, здесь действительно нет разницы, за исключением того, что использование sessionStorageделает его более управляемым для установки точек останова, если вы хотите проверить свою разработку. Не существует истинного аргумента в пользу того, что «лучше», и это просто личное предпочтение, которое ошибается из-за осторожности. Главное, на что следует обратить внимание, это то, что обе sessionStorageи localStorageобе являются реализацией API веб-хранилища HTML5.
DrewT
5

Я столкнулся с той же проблемой в iOS 7 (на некоторых устройствах нет симуляторов).

Похоже, Safari в iOS 7 имеет более низкую квоту хранилища, что, очевидно, достигается за счет длинного журнала истории.

Я думаю, что лучшая практика будет ловить исключение.

Проект Modernizr имеет простой патч, вы должны попробовать что-то похожее: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js

defvol
источник
3

Вот расширенное решение, основанное на ответе DrewT выше, которое использует куки, если localStorage недоступен. Он использует библиотеку Mozilla docCookies :

function localStorageGet( pKey ) {
    if( localStorageSupported() ) {
        return localStorage[pKey];
    } else {
        return docCookies.getItem( 'localstorage.'+pKey );
    }
}

function localStorageSet( pKey, pValue ) {
    if( localStorageSupported() ) {
        localStorage[pKey] = pValue;
    } else {
        docCookies.setItem( 'localstorage.'+pKey, pValue );
    }
}

// global to cache value
var gStorageSupported = undefined;
function localStorageSupported() {
    var testKey = 'test', storage = window.sessionStorage;
    if( gStorageSupported === undefined ) {
        try {
            storage.setItem(testKey, '1');
            storage.removeItem(testKey);
            gStorageSupported = true;
        } catch (error) {
            gStorageSupported = false;
        }
    }
    return gStorageSupported;
}

В вашем источнике просто используйте:

localStorageSet( 'foobar', 'yes' );
...
var foo = localStorageGet( 'foobar' );
...
Stickley
источник
2

Как уже объяснялось в других ответах, в режиме приватного просмотра Safari всегда выдает это исключение при попытке сохранить данные с помощью localStorage.setItem().

Чтобы исправить это, я написал поддельное localStorage, которое имитирует localStorage, как методы, так и события.

Поддельный localStorage: https://gist.github.com/engelfrost/fd707819658f72b42f55

Вероятно, это не очень хорошее общее решение проблемы. Это было хорошим решением для моего сценария, где альтернативой было бы серьезное переписывание уже существующего приложения.

Йозеф Энгельфрост
источник
Что именно это исправляет? Ничего не сохраняется, так какой в ​​этом смысл?
Эсбен Сков Педерсен
1
Он «исправляет» Safari в режиме приватного просмотра. (Это не ясно в моем ответе, спасибо за указание на это. Я отредактирую свой ответ). Ничто не должно сохраняться в режиме приватного просмотра независимо, поэтому отсутствие сохранения не является здесь важной проблемой. Для меня это исправило то, что пользователи могли запускать уже существующее приложение без значительных перезаписей даже в режиме приватного просмотра в Safari.
Йозеф Энгельфрост
2

Обновление (2016-11-01)

Я использовал AmplifyJS, упомянутый ниже, чтобы обойти эту проблему. Тем не менее, для Safari в режиме приватного просмотра он возвращался к памяти на основе памяти. В моем случае это было неуместно, потому что это означает, что хранилище очищается при обновлении, даже если пользователь все еще находится в режиме частного просмотра.

Кроме того, я заметил ряд пользователей, которые всегда смотрят в приватном режиме на iOS Safari. По этой причине лучшим вариантом для Safari является использование файлов cookie (если они доступны). По умолчанию файлы cookie по-прежнему доступны даже при приватном просмотре. Конечно, они очищаются при выходе из частного просмотра, но они не очищаются при обновлении.

Я нашел библиотеку local-storage-fallback . Из документации:

Цель

С настройками браузера, такими как «Приватный просмотр», стало проблемой полагаться на работающий window.localStorage, даже в новых браузерах. Несмотря на то, что он может существовать, он будет выдавать исключения при попытке использовать setItem или getItem. Этот модуль будет запускать соответствующие проверки, чтобы увидеть, какой механизм хранения браузера может быть доступен, а затем выставить его. Он использует тот же API, что и localStorage, поэтому в большинстве случаев он должен работать в качестве замены.

Остерегайтесь ошибок:

  • CookieStorage имеет ограничения по хранению. Будьте осторожны здесь.
  • MemoryStorage не будет сохраняться между загрузками страниц. Это более или менее временный интервал для предотвращения сбоев страниц, но может быть достаточно для веб-сайтов, которые не выполняют полную загрузку страниц.

TL; DR:

Используйте local-storage-fallback (унифицированный API с .getItem(prop)и .setItem(prop, val)):

Проверьте и используйте соответствующий адаптер хранилища для браузера (localStorage, sessionStorage, cookie, память)

Оригинальный ответ

Чтобы добавить к предыдущим ответам, одним из возможных обходных путей может быть изменение метода хранения. Есть несколько библиотек, таких как AmplifyJS и PersistJS, которые могут помочь. Обе библиотеки обеспечивают постоянное хранение на стороне клиента через несколько бэкэндов.

Для AmplifyJS

LocalStorage

  • IE 8+
  • Firefox 3.5+
  • Safari 4+
  • Хром
  • Опера 10.5+
  • iPhone 2+
  • Android 2+

sessionStorage

  • IE 8+
  • Firefox 2+
  • Safari 4+
  • Хром
  • Опера 10.5+
  • iPhone 2+
  • Android 2+

globalStorage

  • Firefox 2+

данные пользователя

  • IE 5 - 7
  • userData существует и в более новых версиях IE, но из-за особенностей реализации IE 9 мы не регистрируем userData, если поддерживается localStorage.

объем памяти

  • Хранилище в памяти предоставляется в качестве запасного варианта, если ни один из других типов хранения не доступен.

Для постоянных JS

  • Вспышка: Flash 8 постоянного хранения.
  • gears: постоянное хранилище на основе Google Gears.
  • localstorage: хранилище черновиков HTML5.
  • globalstorage: хранилище черновиков HTML5 (старая спецификация).
  • то есть: поведение пользовательских данных Internet Explorer.
  • cookie: постоянное хранилище на основе cookie.

Они предлагают уровень абстракции, поэтому вам не нужно беспокоиться о выборе типа хранилища. Имейте в виду, что могут быть некоторые ограничения (например, ограничения размера) в зависимости от типа хранилища. Прямо сейчас я использую AmplifyJS, но мне все еще нужно провести еще какое-то тестирование на iOS 7 / Safari / и т.д. чтобы увидеть, действительно ли это решает проблему.

Джонатан Альзетта
источник
Редактор Джон: Я понимаю, что вы и Джонатан Алзетта, вероятно, являетесь одним и тем же аккаунтом, и вы просто пытаетесь улучшить свой ответ, но если это так, вам действительно нужно войти в систему как Джонатан Алзетта и отредактировать этот ответ, и тогда он не пройдет Очередь обзора. Восстановите свой аккаунт, если вам нужно.
DavidS
0

Этот вопрос и ответ помогли мне решить конкретную проблему с регистрацией новых пользователей в Parse.

Поскольку функция signUp (attrs, options) использует локальное хранилище для сохранения сеанса, если пользователь находится в режиме частного просмотра, он выдает «QuotaExceededError: Исключение DOM 22: была предпринята попытка добавить в хранилище что-то, превышающее квоту». Функции исключения и успеха / ошибки никогда не вызываются.

В моем случае, так как функция ошибки никогда не вызывается, изначально возникла проблема с активацией события click для подтверждения или перенаправления, определенного при успешной регистрации.

Включение предупреждения для пользователей решило проблему.

Разбор ссылки на Javascript SDK https://parse.com/docs/js/api/classes/Parse.User.html#methods_signUp

Регистрация нового пользователя с именем пользователя (или адрес электронной почты) и паролем. Это создаст новый Parse.User на сервере, а также сохранит сеанс в localStorage, чтобы вы могли получить доступ к пользователю, используя {@link #current}.

clayostrom
источник