计算 localStorage 空间的使用情况

我正在使用 Bespin 编辑器和 HTML5的 localStorage 创建一个应用程序。它在本地存储所有文件并帮助处理语法,使用 JSLint 和其他一些 CSS 和 HTML 解析器来帮助用户。

我想计算使用了 localStorage 限制的多少,以及实际使用了多少。我考虑的不是简单地计算存储的比特数。但话说回来,我不确定还有什么是我无法衡量的。

65032 次浏览

IE8 implements the remainingSpace property for this purpose:

alert(window.localStorage.remainingSpace);  // should return 5000000 when empty

Unfortunately it seems that this is not available in the other browsers. However I am not sure if they implement something similar.

I didn't find a universal way to get the remaining limit on the browsers I needed, but I did find out that when you do reach the limit there is an error message that pops up. This is of-course different in each browser.

To max it out I used this little script:

for (var i = 0, data = "m"; i < 40; i++) {
try {
localStorage.setItem("DATA", data);
data = data + data;
} catch(e) {
var storageSize = Math.round(JSON.stringify(localStorage).length / 1024);
console.log("LIMIT REACHED: (" + i + ") " + storageSize + "K");
console.log(e);
break;
}
}
localStorage.removeItem("DATA");

From that I got this information:

Google Chrome

  • DOMException:
    • code: 22
    • message: "Failed to execute 'setItem' on 'Storage': Setting the value of 'data' exceeded the quota."
    • name: "QuotaExceededError"

Mozilla Firefox

  • DOMException:
    • code: 1014
    • message: "Persistent storage maximum size reached"
    • name: "NS_ERROR_DOM_QUOTA_REACHED"

Safari

  • DOMException:
    • code: 22
    • message: "QuotaExceededError: DOM Exception 22"
    • name: "QuotaExceededError"

Internet Explorer, Edge (community)

  • DOMException:
    • code: 22
    • message: "QuotaExceededError"
    • name: "QuotaExceededError"

My solution

So far my solution is to add an extra call each time the user would save anything. And if the exception is caught then I would tell them that they are running out of storage capacity.


Edit: Delete the added data

I forgot to mention that for this to actually work you would need to delete the DATA item that was set originally. The change is reflected above by using the removeItem() function.

You may be able to get an approximate idea by using the JSON methods to turn the whole localStorage object to a JSON string:

JSON.stringify(localStorage).length

I don't know how byte-accurate it would be, especially with the few bytes of added markup if you're using additional objects - but I figure it's better than thinking you're only pushing 28K and instead doing 280K (or vice-versa).

To add to the browser test results:

Firefox i=22.

Safari Version 5.0.4 on my Mac didn't hang. Error as Chrome. i=21.

Opera Tells the user that the website wants to store data but doesn't have enough space. The user can reject the request, up the limit to the amount required or to several other limits, or set it to unlimited. Go to opera:webstorage to say whether this message appears or not. i=20. Error thrown is same as Chrome.

IE9 standards mode Error as Chrome. i=22.

IE9 in IE8 standards mode Console message "Error: Not enough storage is available to complete this operation". i=22

IE9 in older modes object error. i=22.

IE8 Don't have a copy to test, but local storage is supported (http://stackoverflow.com/questions/3452816/does-ie8-support-out-of-the-box-in-localstorage)

IE7 and below Doesn't support local storage.

You can use the below line to accurately calculate this value and here is a jsfiddle for illustration of its use

alert(1024 * 1024 * 5 - escape(encodeURIComponent(JSON.stringify(localStorage))).length);

Ran into this today while testing (exceeding storage quota) and whipped up a solution. IMO, knowing what the limit is and where we are in relation is far less valuable than implementing a functional way to continue storing beyond the quota.

Thus, rather than trying to do size comparisons and capacity checks, lets react when we've hit the quota, reduce our current storage by a third, and resume storing. If said reduction fails, stop storing.

set: function( param, val ) {
try{
localStorage.setItem( param, typeof value == 'object' ? JSON.stringify(value) : value )
localStorage.setItem( 'lastStore', new Date().getTime() )
}
catch(e){
if( e.code === 22 ){
// we've hit our local storage limit! lets remove 1/3rd of the entries (hopefully chronologically)
// and try again... If we fail to remove entries, lets silently give up
console.log('Local storage capacity reached.')


var maxLength = localStorage.length
, reduceBy = ~~(maxLength / 3);


for( var i = 0; i < reduceBy; i++ ){
if( localStorage.key(0) ){
localStorage.removeItem( localStorage.key(0) );
}
else break;
}


if( localStorage.length < maxLength ){
console.log('Cache data reduced to fit new entries. (' + maxLength + ' => ' + localStorage.length + ')');
public.set( param, value );
}
else {
console.log('Could not reduce cache size. Removing session cache setting from this instance.');
public.set = function(){}
}
}
}
}

This function lives within a wrapper object, so public.set simply calls itself. Now we can add to storage and not worry what the quota is or how close we are too it. If a single store is exceeding 1/3rd the quota size is where this function will stop culling and quit storing, and at that point, you shouldn't be caching anyways, right?

Wish I could add this in a comment - not enough rep, sorry.

I ran some perf tests - expecting JSON.stringify(localStorage).length to be an expensive op at large localStorage occupancy.

http://jsperf.com/occupied-localstorage-json-stringify-length

It is indeed so - about 50x more expensive than keeping track of what you're storing, and gets worse the fuller localStorage gets.

You can test your browser with this web storage support test

I tested Firefox on both my android tablet and windows laptop and Chromium just on windows results:

  1. Firefox(windows):
  • localStorage: 5120k char
  • sessionStorage: 5120k char
  • globalStorage: *not supported
  1. Firefox(android):
  • localStorage: 2560k char
  • sessionStorage: Unlimited (exactly test runs up to 10240k char == 20480k byte)
  • globalStorage: not supported
  1. Chromium(windows):
  • localStorage: 5120k char
  • sessionStorage: 5120k char
  • globalStorage: not supported

###Update On Google Chrome Version 52.0.2743.116 m (64-bit) limits where a little bit lower on 5101k characters. This means max available may change in versions.

This might help somebody. In chrome is possible to ask the user to allow to use more disk space if needed:

// Request Quota (only for File System API)
window.webkitStorageInfo.requestQuota(PERSISTENT, 1024*1024, function(grantedBytes) {
window.webkitRequestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler);
}, function(e) {
console.log('Error', e);
});

Visit https://developers.google.com/chrome/whitepapers/storage#asking_more for more info.

This function gets the exact storage available / left:

I made a suite of useful functions for localStorage *here*

http://jsfiddle.net/kzq6jgqa/3/

function getLeftStorageSize() {
var itemBackup = localStorage.getItem("");
var increase = true;
var data = "1";
var totalData = "";
var trytotalData = "";
while (true) {
try {
trytotalData = totalData + data;
localStorage.setItem("", trytotalData);
totalData = trytotalData;
if (increase) data += data;
} catch (e) {
if (data.length < 2) break;
increase = false;
data = data.substr(data.length / 2);
}
}
localStorage.setItem("", itemBackup);


return totalData.length;
}


// Examples
document.write("calculating..");
var storageLeft = getLeftStorageSize();
console.log(storageLeft);
document.write(storageLeft + "");


// to get the maximum possible *clear* the storage
localStorage.clear();
var storageMax = getLeftStorageSize();

Note, that this is not very quick, so don't use it all the time.

With this I also found out that: the Item-Name will take up as much space as its length, the Item-Value will also take up as much space as their length.

Maximum storage I got - all about 5M:

  • 5000000 chars - Edge
  • 5242880 chars - Chrome
  • 5242880 chars - Firefox
  • 5000000 chars - IE

You will find some out-commented code in the fiddle to see the progress in the console.

Took me some time to make, hope this helps ☺

I needed to actually simulate and test what my module will do when storage is full, so I needed to get a close precision on when the storage is full, rather than the accepted answer, which loses that precision at a rate of i^2.

Here's my script, which should always produce a precision of 10 on when memory cap is reached, and fairly quickly despite having some easy optimizations... EDIT: I made the script better and with an exact precision:

function fillStorage() {
var originalStr = "1010101010";
var unfold = function(str, times) {
for(var i = 0; i < times; i++)
str += str;
return str;
}
var fold = function(str, times) {
for(var i = 0; i < times; i++) {
var mid = str.length/2;
str = str.substr(0, mid);
}
return str;
}


var runningStr = originalStr;
localStorage.setItem("filler", runningStr);
while(true) {
try {
runningStr = unfold(runningStr, 1);
console.log("unfolded str: ", runningStr.length)
localStorage.setItem("filler", runningStr);
} catch (err) {
break;
}
}


runningStr = fold(runningStr, 1);
var linearFill = function (str1) {
localStorage.setItem("filler", localStorage.getItem("filler") + str1);
}
//keep linear filling until running string is no more...
while(true) {
try {
linearFill(runningStr)
} catch (err) {
runningStr = fold(runningStr, 1);
console.log("folded str: ", runningStr.length)
if(runningStr.length == 0)
break;
}
}


console.log("Final length: ", JSON.stringify(localStorage).length)
}
 try {
var count = 100;
var message = "LocalStorageIsNOTFull";
for (var i = 0; i <= count; count + 250) {
message += message;
localStorage.setItem("stringData", message);
console.log(localStorage);
console.log(count);
}


}
catch (e) {
console.log("Local Storage is full, Please empty data");
// fires When localstorage gets full
// you can handle error here ot emply the local storage
}

This script will return false if local storage is not available or return exact available space in local storage to the nearest character, regardless of if the browser throws an error or simply doesn't add anything to local storage when local storage is full.

var localstorageavail;
function localstoragetest(remaining) {
if (typeof(Storage) !== "undefined") {
localstorageavail = true;
var usedspace = JSON.stringify(localStorage).length;
if (remaining == true) {
var unusedspace = 0, data = "m", adddata, stored = 0;
for (adddata = "m";;) {
try {
localStorage.setItem("UN", data);
if (stored < JSON.stringify(localStorage).length) {
stored = JSON.stringify(localStorage).length;
adddata += adddata;
data += adddata;
}
else throw "toolong";
} catch(e) {
if (adddata == "m") break;
else adddata = "m";
data += adddata;
}
}
var totalspace = JSON.stringify(localStorage).length;
unusedspace = totalspace - usedspace;
localStorage.removeItem("UN");
alert("Space Used Calculated: " + usedspace + " " + "\nUnused space: " + unusedspace + "\nSpace Used according to browser: " + JSON.stringify(localStorage).length)
}
} else {
//    alert("Sorry! No Web Storage support..");
localstorageavail = false;
}
if (localstorageavail == false) return localstorageavail;
else return unusedspace;
}
localstoragetest(true);