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


133

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

JavaScript:エラー

未定義

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

私のコード:

localStorage.setItem('test',1)

この特定の問題をテストする機能検出を使用します。ストレージが利用できない場合は、localStorageをmemoryStorageでシミングすることを検討してください免責事項:私はリンクされたパッケージの作成者です
Stijn de Witt 2017

4
こんにちは皆さん、私はサファリドライバーのメンテナンスを手伝います。この問題は、最近修正されたWebKitの長年のバグです。ローカルストレージとセッションストレージがSafari 10.1以降で機能するようになりました。この修正は、通常のプライベートブラウジングモードとオートメーションモード(WebDriverで使用)に影響します。
ブライアンバーグ2017

回答:


183

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

1
これは必ずしもシークレットモードが原因である必要はありません... OPが数メガバイトのデータを保存したくなかったと思いますが;)
Christoph

5
Paul Irishによるローカルストレージの検出の簡単な履歴を示す要点を確認してください。
Mottie 14

4
localStorageがSafariで機能しない場合、すべてをCookieに保存するのが次善の策でしょうか?
ヒッチコック

5
ポールアイリッシュの例と同様に、に変更return localStorageName in win && win[localStorageName];することをお勧めしreturn trueます。次に、localStorageの可用性に応じてtrueまたはfalseを安全に返す関数があります。次に例を示しますif (isLocalStorageNameSupported()) { /* You can use localStorage.setItem */ } else { /* you can't use localStorage.setItem */ }
。– DrewT

1
この問題がプライベートウィンドウだけでなく、通常のSafariウィンドウでも発生することを確認。
コードミラー2017年

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


20
あなた(および@KingKongFrog)がwindow.sessionStorageを使用して、localStorageに書き込むことができるかどうか、または奇妙なコピーと貼り付けのタイポサイクルにあるかどうかを特定する理由はありますか?
イエッティ2014

@Yettiタイプミスに気づいた場合は、編集またはコメントで修正してみませんか?私の知る限りでwindow.sessionStorageは正しいです。それは確かに私のコードで動作します。あなたが知っていると思われる問題の修正方法を実際に指摘してください。
Novocaine、

7
@Novocaine私のコメントは、localStorageサポートをチェックするために存在する関数でsessionStorageを使用していることを指摘していました。はい、おそらく機能しますが、書かれているように、関数名は実際にテストされているものに対して誤解を招きます。私は何かを逃していると思って、これらの人から学ぶことを望んでいたので、編集ではなくコメントすることを選びました。残念ながら、彼らは返信も訂正もしていないので、ここにあります。
Yetti

3
@Yetti明確にしていただきありがとうございます。あなたが今何をしていたのか分かります。;-]
Novocaine、2015年

2
@DawsonTothいいえ、それは私が関数を呼び出してisLocalStorageNameSupportedチェックしていたためwindow.sessionStorageです。最終結果は同じですが、少し混乱しました。回答が明確になるように編集されました。
cyberwombat 2017年

25

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

1つの解決策は、使用の各インスタンスで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.');
    }
}

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

2
ピエールに感謝します。あなたの答えが私に影響を与えました。私はそれをすべてmemorystorageという素晴らしいモジュールにパッケージ化しました。もちろんオープンソース。同じ問題を抱えている他の人たちのために、それがあなたを助けるかもしれないことをチェックしてください。
Stijn de Witt 2015

ハ。私は(独立して)同じことをしました。それでもlocalStorage変数を使用します(少なくともSafariでは、localStorage変数を上書きすることはできません(「読み取り専用」です)が、setItem / removeItem / getItemを再割り当てできます)。
ルーベンマルティネスジュニア

@StijndeWitt、他のページのストレージ値にアクセスするにはどうすればよいですか?たとえば、これは私のhelper.phpにありますvar store = MemoryStorage( 'my-app'); store.setItem( 'myString'、 'Hello MemoryStorage!'); Lecture.phpのmyStringの値にアクセスしたいのですが。ページでメモリストレージを開始しようとしましたが、それでも空のオブジェクトが表示されます。
user1149244 2017

@ user1149244 Memorystorageはページに対してローカルです。Web Storage APIをシミュレートするため、localStorageとsessionStorageが使用できない場合のフォールバックとして使用できます。ただし、データはページメモリ(したがって名前)にのみ保持されます。ページ間でデータを保持する必要がある場合は、Cookieが役立ちます。ただし、保存できるデータ量は非常に限られています。それ以外にできることはあまりありません。
Stijn de Witt 2017

2
@ user1149244 これはおそらくブラウザに値を格納しているのではないですか?いいえ、できません。あるページの更新から別のページへの更新をクライアント側に保存するには、Cookie、sessionStorage / localStorage、IndexedDBの3つの方法があります。最後の2つは比較的新しいものです。sessionStorageとlocalStorageは幅広くサポートされているため、基本的にどこでも使用できます。プライベートブラウジングモードを除いて、これがこの問題に関するものです。ストレージがなかったため、プログラムが壊れました。memorystorageは、ページ上で常に機能するフォールバックを提供するだけですが、実際にデータを保存することはできません。それはスタブです。しかし、エラーはありません。
Stijn de Witt 2017

5

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

以前はSafariのプライベートブラウジングで失敗していた私たちのWebアプリが問題なく動作するようになりました。それは常にローカルストレージへの書き込みを許可していたChromeのプライベートブラウジングモードで常に正常に動作しました。

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

具体的には:

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

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

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

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


1
これは質問の答えにはなりませんが、これは私が問題をグーグルで調べたときに最初に出てきたものです。次のステップは、Angularのソリューションを検索することでしたが、このコメントのおかげで、他の場所に行く必要はありません。だから、質問に直接答えないかもしれませんが、私にとって、そしておそらく他の人にとっては素晴らしいことでした!
Leonard

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

1

このリポジトリを作成sessionStoragelocalStorageて、サポートされていない、または無効になっているブラウザに機能を提供しました。

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

  • 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;
    }
};

サポートされているか、Cookieストレージを作成するかを設定StorageService.localStoragewindow.localStorageます。サポートさStorageService.sessionStorageれているwindow.sessionStorage場合、またはSPAのメモリ内ストレージ、非SPAのセッション機能を備えたCookieストレージを作成する場合に設定します。


1
ありがとう、あなたの図書館は大いに助けてくれました!
Mathieu

1

これはメモリストレージの代替としてAngular2 +サービスバージョンです。ピエールルルーの回答に基づいて、コンポーネントに注入するだけです。

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

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

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

これにより、すべてのブラウザーでキーが正しく設定および取得されます。


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

0

受け入れられた回答は、いくつかの状況では適切ではないようです。

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

このスニペットを次のように使用し、たとえばcookieを使用するようにフォールバックします。

if (storageAvailable('localStorage')) {
  // Yippee! We can use localStorage awesomeness
}
else {
  // Too bad, no localStorage for us
  document.cookie = key + "=" + encodeURIComponent(value) + expires + "; path=/";
}

このスニペットを使用して、ストレージの可用性と手動で実装されたMemoryStorageへのフォールバックを確認するfallbackstorageパッケージを作成しました。

import {getSafeStorage} from 'fallbackstorage'

getSafeStorage().setItem('test', '1') // always work

-1
var mod = 'test';
      try {
        sessionStorage.setItem(mod, mod);
        sessionStorage.removeItem(mod);
        return true;
      } catch (e) {
        return false;
      }

1
多分あなたは説明のいくつかの単語を追加したいですか?
bogl 2018年

-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の代わりに使用します。さらに情報が必要な場合はお知らせください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.