web-dev-qa-db-ja.com

Safariでのhtml5 localStorageエラー:「QUOTA_EXCEEDED_ERR:DOM例外22:クォータを超えたストレージに何かを追加しようとしました。」

私のwebappには、ios safariプライベートブラウジングでjavascriptエラーがあります:

JavaScript:エラー

undefined

QUOTA_EXCEEDED_ERR:DOM例外22:ストレージに何かを追加しようとしました...

私のコード:

localStorage.setItem('test',1)
131
leiyonglin

どうやらこれは仕様によるものです。 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;
    }
}
183
KingKongFrog

上記のリンクに投稿された修正は、私にとってはうまくいきませんでした。これは:

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 から派生

37
cyberwombat

他の回答で述べたように、localStorage.setItem(またはsessionStorage.setItem)が呼び出されると、iOSとOS Xの両方でSafariプライベートブラウザーモードで常にQuotaExceededErrorが発生します。

1つの解決策は、setItemを使用する各インスタンスでtry/catchまたは Modernizr check を実行することです。

ただし、このエラーがスローされるのを単にグローバルに停止するシムが必要な場合は、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.');
    }
}
24
philfreo

私の文脈では、クラスの抽象化を開発しました。アプリケーションが起動したら、getStorage()を呼び出してlocalStorageが動作しているかどうかを確認します。この関数も返します:

  • localStorageが機能している場合はlocalStorage
  • またはカスタムクラスの実装LocalStorageAlternative

私のコードでは、localStorageを直接呼び出すことはありません。 cusSto global 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();
11
Pierre Le Roux

他の人の答えを拡張するために、新しい変数を公開/追加しないコンパクトなソリューションを以下に示します。すべてのベースを網羅しているわけではありませんが、単一ページのアプリが機能し続けることを望むほとんどの人に適しているはずです(リロード後のデータの永続性はありません)。

(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 = {};
        };
    }
})();
4
Jon

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

コーディングをお楽しみください!

3
jorgecasar

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');
      }
    };
  }
]);
2

Safari 11が動作を変更し、ローカルストレージがプライベートブラウザウィンドウで機能するようになったようです。やった!

Safariのプライベートブラウジングで失敗していたWebアプリは、今では問題なく動作します。 Chromeのプライベートブラウジングモードでは常に正常に機能し、常にローカルストレージへの書き込みが許可されています。

これは、Appleの Safari Technology Previewリリースノート -および WebKitリリースノート -2017年5月のリリース29に記載されています。

具体的には:

  • プライベートブラウジングモードまたはWebDriverセッションでlocalStorageに保存する場合のQuotaExceededErrorを修正- r215315
2
karlbecker_com

サポートされていないブラウザまたは無効なブラウザにsessionStorageおよびlocalStorage機能を提供するために、この repo を作成しました。

サポートされているブラウザ

  • 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ストレージを作成します。

1

サポートされていない場合は使用しないでください。サポートを確認するには、この関数を呼び出してください。

es6での共有読み取りと書き込みサポートlocalcheckの例

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);

これにより、すべてのブラウザでキーが適切に設定および取得されるようになります。

0
Tarandeep Singh

これは、メモリストレージの代替として使用できるAngular2 +サービスバージョンです。PierreLe 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);
    }

}
0
Gabriel Alack
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }
0
Naim DOGAN