37

I've written a webapp that allows you to store the images in the localStorage until you hit save (so it works offline, if signal is poor).

When the localStorage reaches 5MB Google Chrome produces an error in the javascript console log:

Uncaught Error: QUOTA_EXCEEDED_ERR: DOM Exception 22

How do I increase the size of the localStorage quota on Google Chrome?

HM2K
  • 1,461
  • 4
  • 16
  • 21
  • See: http://stackoverflow.com/questions/5302212/is-there-any-way-a-google-chrome-extension-can-increase-local-storage-space – PaoloVictor Apr 14 '11 at 12:24
  • Actually, see http://stackoverflow.com/a/6281947/632951 – Pacerier Jun 23 '15 at 02:51
  • Relevant: [Storing Compressed JSON Objects in Local Storage With Lz-string](http://undefinedmethod.com/blog/storing-compressed-json-objects-in-local-storage-with-lz-string/) and [Calculating usage of localStorage space](https://stackoverflow.com/questions/3027142/calculating-usage-of-localstorage-space/3027249#3027249) – brasofilo Nov 13 '19 at 03:27

7 Answers7

24

5MB is a hard limit and that is stupid. IndexedDB gives you ~50MB which is more reasonable. To make it easier to use try Dexie.js https://github.com/dfahlander/Dexie.js

Update:

Dexie.js was actually still an overkill for my simple key-value purposes so I wrote this much simpler script https://github.com/DVLP/localStorageDB

with this you have 50MB and can get and set values like that

// Setting values
ldb.set('nameGoesHere', 'value goes here');

// Getting values - callback is required because the data is being retrieved asynchronously:
ldb.get('nameGoesHere', function (value) {
  console.log('And the value is', value);
});

Copy/paste the line below so ldb.set() and ldb.get() from the example above will become available.

!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
danwellman
  • 9,068
  • 8
  • 60
  • 88
Pawel
  • 16,093
  • 5
  • 70
  • 73
  • When using this method, how can I get all the stored keys? – tjespe Jan 09 '17 at 14:34
  • @tjespe just like it shows in the example `ldb.get('nameGoesHere', function (value) { console.log('And the value is', value) }); ` – Pawel Jan 09 '17 at 21:04
  • @Pawel I am getting: Cannot read property 'transaction' of undefined –  May 25 '17 at 18:43
  • 1
    Very useful! I only wish it allowed listing unknown entries that exist in IndexedDB. (Analogy: `Object.keys(localStorage)`.) – 7vujy0f0hy Nov 21 '17 at 16:14
  • It's not stupid if you don't have a way to do lazy-loading of the stored elements -- after all, the storage is accessed as properties on the `localStorage` (or `sessionStorage`) object. You are assuming lazy loading, which indeed, would make the limit more "artificial". – Armen Michaeli Nov 09 '20 at 14:12
  • 3
    this is just Amazing – Bouh Sep 03 '21 at 15:34
18

You can't, it's hard-wired at 5MB. This is a design decision by the Chrome developers.

In Chrome, the Web SQL db and cache manifest also have low limits by default, but if you package the app for the Chrome App Store you can increase them.

See also Managing HTML5 Offline Storage - Google Chrome.

brasofilo
  • 25,496
  • 15
  • 91
  • 179
Liza Daly
  • 2,933
  • 23
  • 31
  • 2
    5MB with UTF16 encoding . so environ 2.5 M characters. – Emmanuel Devaux Mar 26 '14 at 08:44
  • 6
    is this still true?! so is still 5MB the limit of localstorage?! – eKelvin Oct 03 '14 at 15:36
  • 1
    @Michael me too. This is just stupid. I'm generating GIFs on client and want to store them to not re-generate them every time. IndexedDB is a solution but it's an overkill in my case. Dexie.js seems to help so I'll try that out – Pawel May 17 '16 at 13:20
3

You can't but if you save JSON in your localStorage you can use a library to compress data like : https://github.com/k-yak/JJLC

demo : http://k-yak.github.io/JJLC/

  • 1
    Seems poor compression. I tried a 4MB JSON and JJLC only reduced it to 68% of its original. By comparison gzip reduced it to 5%. – James Mar 08 '18 at 13:03
2

The quota is for the user to set, how much space he wishes to allow to each website.

Therefore since the purpose is to restrict the web pages, the web pages cannot change the restriction.

If storage is low, you can prompt the user to increase local storage.

To find out if storage is low, you could probe the local storage size by saving an object then deleting it.

Ben
  • 34,935
  • 6
  • 74
  • 113
  • 25
    yes, so how do you, the *user* set the quota in chrome? – Michael Mar 03 '14 at 15:15
  • 1
    Not about chrome? The question is explicitly about Chrome, it is mentioned in the title, the question, and it tagged with google-chrome. As for prompting the user, Chrome does not do this, and without some mechanism to change the quota in javascript or even a manual way by the user it hardly seems useful to write code to prompt the user. – Michael Mar 05 '14 at 14:03
  • @Michael, according to the chrome documentation https://developers.google.com/chrome/whitepapers/storage#managing_quota it does in fact prompt the user if you ask for filesystem storage. – Ben Mar 05 '14 at 19:00
  • 8
    localStorage isn't accessed through the filesystem API, it's accessed via the window.localStorage variable. You don't get a prompt if you exceed it, you just get a silent failure (unless you've tested for/trapped the condition). – Michael Mar 05 '14 at 21:42
  • 1
    that page doesn't seem to do a good job at all documenting localStorage, it only mentions it once in passing, and doesn't really say definitely where if anywhere it fits in any of the models it presents. (and it doesn't really seem to fit completely in any of them, from what I can tell...) – Michael Mar 05 '14 at 21:46
1

Here you can test your program , you should handle also the cases when the cuota is exceed enter image description here

1

https://stackoverflow.com/a/5664344/2630686 The above answer is much amazing. I applied it in my project and implement a full solution to request all kinds of resource.

// Firstly reference the above ldb code in the answer I mentioned.
export function get_file({ url, d3, name, enable_request = false }) {
  if (name === undefined) { // set saved data name by url parsing alternatively
    name = url.split('?')[0].split('/').at(-1).split('.')[0];
  }
  const html_name = location.href.split('/').at(-1).split('.')[0]
  name = `${html_name}_${name}`
  let ret = null;
  const is_outer = is_outer_net(url); // check outer net url by its start with http or //
  // try to access data from local. Return null if not found
  if (is_outer && !enable_request) {
    if (localStorage[name]) {
      ret = new Promise(resolve => resolve(JSON.parse(localStorage[name])));
    } else {
      ret = new Promise(r => {
        ldb.get(name, function (value) {
          r(value)
        })
      });
    }
  } else {
    ret = new Promise(r => r(null))
  }
  ret.then(data => {
    if (data) {
      return data
    } else {
      const method = url.split('.').at(-1)
      // d3 method supported
      if (d3 && d3[method]) {
        ret = d3[method](url)
      } else {
        if (url.startsWith('~/')) { // local files accessed supported. You need a local service that can return local file data by requested url's address value
          url = `http://localhost:8010/get_file?address=${url}`
        }
        ret = fetch(url).then(data => {
          // parse data by requested data type
          if (url.endsWith('txt')) {
            return data.text()
          } else {

            return data.json()
          }
        })
      }
      ret = ret.then(da => {
        data = da
        if (is_outer) { // save data to localStorage firstly
          localStorage[name] = JSON.stringify(data);
        }
      }).catch(e => { // save to ldb if 5MB exceed
        ldb.set(name, data);
      }).finally(_ => {

        return data;
      });
    }
  })
  return ret;
}
Youth overturn
  • 341
  • 5
  • 7
0

I have also encountered the same issue. After careful consideration, I believe that migrating from localStorage to other types of local storage, such as indexedDB, is a better approach when dealing with large amounts of data.

The reason is that localStorage operates on blocking synchronous processes. Storing a significant amount of data using synchronous processes is generally not recommended (which is likely why the Chrome developer team is adamant about not allowing users or developers to increase the 5MB hard limit of localStorage).

However, let's be honest... we appreciate the simplicity, ease of use, and straightforwardness of the localStorage API. It is unlike indexedDB in those aspects.

Therefore, I recommend using an alternative library: https://github.com/dreamsavior/Better-localStorage

This library is designed to be similar to localStorage, making the migration process easy:

const MyLocalStorage = require('better-localstorage');
await myStorage.setItem("someKey", { "someObject": "with the value" });
const value = await myStorage.getItem("someKey");
console.log(value);

Note that you don't need to convert the JavaScript object into JSON.

Using the native API, you would need to do the following:

myStorage.setItem("someKey", JSON.stringify({ "someObject": "with the value" }));
const value = JSON.parse(myStorage.getItem("someKey"));
console.log(value);
Donovan P
  • 591
  • 5
  • 9