I am creating an app using the Bespin editor and HTML5's localStorage. It stores all files locally and helps with grammar, uses JSLint and some other parsers for CSS and HTML to aid the user.
I want to calculate how much of the localStorage limit has been used and how much there actually is. Is this possible today? I was thinking for not to simply calculate the bits that are stored. But then again I'm not sure what more is there that I can't measure myself.
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).
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.
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.
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?
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.
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.
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 ☺
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:
Firefox(windows):
localStorage: 5120k char
sessionStorage: 5120k char
globalStorage: *not supported
Firefox(android):
localStorage: 2560k char
sessionStorage: Unlimited (exactly test runs up to 10240k char == 20480k byte)
globalStorage: not supported
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.
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 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 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);
Related
In Photoshop I can read in a text file.
function does_file_exist(f)
{
var lines = "";
var aFile = new File(f);
aFile.open("r");
while(!aFile.eof)
{
var line = aFile.readln();
if (line != null && line.length >0)
{
lines += line + "\n";
}
}
aFile.close();
if (lines.length == 0)
{
alert(f + "\ndoes not exist!");
return false;
}
else
{
var trunc = lines.substring(0,256);
alert(f + " exists!\nHere's proof:\n\n" + trunc + "...")
return lines;
}
}
If the string returned has a length of 0 we assume that the file simply doesn't exist. This works fine, but what happens if there is an empty file of 0 bytes? Can I access the filesize properties? Or is there another way around this problem? There seem to be problems with file.exists()
File(f).exists
Is a Boolean not a function
Boolean exists Read only Property
If true, this object refers to a file or file-system alias that actually exists in the file system.
Update: Actually "Mr. Mystery Guest" is right (see comments).
File('~/Desktop/does-not-exist.txt').exists
returns true for me on macOS 10.12.2 and PS CC2017 even though the file does not exists. Using
new File('~/Desktop/does-not-exist.txt').exists
seems to work as excepted.
Update 2:
This error seems to be a Photoshop specific problem. In ESTK and in InDesign File('~/Desktop/does-not-exist.txt').exists returns false
I am trying to save / restore the scrolling location on Postbacks.
My code works for Firefox and all major browsers except for Internet Explorer.
function saveScrollPosition() {
// Save the cookie if the requestor is Internet Explorer
if (navigator.appName.indexOf("Microsoft") != -1) {
alert("Internet Explorer browser has been identified...");
var scrollX, scrollY;
var strA = "KulScrollPos=";
var strB = ",";
var strC = "; path=";
// Depending on the version of Internet Explorer --- call the appropriate API
if (!document.documentElement.scrollLeft)
scrollX = document.body.scrollLeft;
else
scrollX = document.documentElement.scrollLeft;
if (!document.documentElement.scrollTop)
scrollY = document.body.scrollTop;
else
scrollY = document.documentElement.scrollTop;
alert("scrollX = " + scrollX + " and " + "scrollY = " + scrollY);
alert("strA = " + strA);
//document.cookie = "KulScrollPos="+scrollX+","+scrollY+"; path="+document.location.pathname;
document.cookie = strA.concat(scrollX, strB, scrollY, strC, document.location.pathname);
}
// Save the cookie for all other major browsers
else {
document.cookie = "KulScrollPos="+f_scrollLeft()+","+f_scrollTop()+"; path="+document.location.pathname;
}
alert("cookie = " + document.cookie)
}
function restoreScrollPosition() {
alert("Entered the restore method...");
cookieName = "KulScrollPos";
if (document.title == "KFS :: Create Purchase Log") {
resetScrollPosition();
expireCookie( cookieName );
return true;
}
else {
var matchResult = document.cookie.match(new RegExp(cookieName+"=([^;]+);?"));
if ( matchResult ) {
var coords = matchResult[1].split( ',' );
if (coords[1] != 0) {
alert("Restoring the scroll position before scrollTo... " + coords[0] + " and " + coords[1]);
window.scrollTo(coords[0],coords[1]);
parent.window.scrollTo(coords[0],coords[1]);
}
expireCookie( cookieName );
return true;
}
else {
return false;
}
}
Notice my alert box where I am printing the cookie name.
Firefox prints the following:
cookie = KulScrollPos=0,1946; kualiSessionId=A7807919-4719-D5B4-91D6-9CC04EEA1BA8;JSESSIONID=1F155C7FC23C48A4DAF557CA4B92D2CB
Internet Explorer prints the following:
cookie = kualiSessionId=072BE31C-6AF5-6D4C-11A4-55E799790C6A; JSESSIONID=76D83E8E7EBA5F25B8A1B1990B9344E8
Notice that the string
KulScrollPos=0,1946;
is being left off the cookie name. This only happens in Internet Explorer!
***I tried another approach at setting the string variable (the line that is commented out) where I am setting document.cookie = ... This line also produced the same alert output as displayed above.
Notice my ELSE Block in my RestoreScrollPosition.
The if (matchResult) condition always fails because of this, because of which, my code there where I call my scrollTo method never gets called!
Ughhh, am I concatenating the strings wrong? What doesn't IE like that FF does?
Very strange behavior indeed!
Apparently Internet Explorer doesn't like the "=" (equal sign) in cookie names that is provided within double quotes. It was interpreting the '='; rather than accepting it as a literal; thus, I solved the problem using the single quotes. Apparently, you have to be forceful with IE! Go figure....
The following code fixed the problem that I was having -
function saveScrollPosition() {
// Save the cookie if the requesting browser is Internet Explorer
if (navigator.appName.indexOf("Microsoft") != -1) {
// Ensure that the cookie will be saved on IE version 5/+
if (!document.documentElement.scrollLeft)
scrollX = document.body.scrollLeft;
else
scrollX = document.documentElement.scrollLeft;
if (!document.documentElement.scrollTop)
scrollY = document.body.scrollTop;
else
scrollY = document.documentElement.scrollTop;
document.cookie = 'KulScrollPos =' + scrollX+','+scrollY+';'+document.location.pathname;
}
// Save the cookie for all other major browsers
else {
document.cookie = "KulScrollPos="+f_scrollLeft()+","+f_scrollTop()+"; path="+document.location.pathname;
}
}
Lessons learned -
Don't use "=" signs in your cookie names. If you need them, use the single quotes to tell IE not to interpret it, but to accept it as a literal.
Cookie data is not supposed to be able to contain commas. You'll need to encode or escape your scroll data before writing it, and then decode or unescape it on read.
Edit: You could also just change your delimiter; maybe try a pipe ( | )?
Comma is valid separator for multiple cookies. Try replace comma to %2C, or escape() whole cookie value.
I am currently developing a site that will make use of HTML5's localStorage. I've read all about the size limitations for different browsers. However, I haven't seen anything on how to find out the current size of a localStorage instance. This question seems to indicate that JavaScript doesn't have a built in way of showing the size for a given variable. Does localStorage have a memory size property that I haven't seen? Is there an easy way to do this that I'm missing?
My site is meant to allow users to enter information in an 'offline' mode, so being able to give them a warning when the storage is almost full is very important.
Execute this snippet in JavaScript console (one line version):
var _lsTotal=0,_xLen,_x;for(_x in localStorage){ if(!localStorage.hasOwnProperty(_x)){continue;} _xLen= ((localStorage[_x].length + _x.length)* 2);_lsTotal+=_xLen; console.log(_x.substr(0,50)+" = "+ (_xLen/1024).toFixed(2)+" KB")};console.log("Total = " + (_lsTotal / 1024).toFixed(2) + " KB");
The same code in multiple lines for reading sake
var _lsTotal = 0,
_xLen, _x;
for (_x in localStorage) {
if (!localStorage.hasOwnProperty(_x)) {
continue;
}
_xLen = ((localStorage[_x].length + _x.length) * 2);
_lsTotal += _xLen;
console.log(_x.substr(0, 50) + " = " + (_xLen / 1024).toFixed(2) + " KB")
};
console.log("Total = " + (_lsTotal / 1024).toFixed(2) + " KB");
or add this text in the field 'location' of a bookmark for convenient usage
javascript: var x, xLen, log=[],total=0;for (x in localStorage){if(!localStorage.hasOwnProperty(x)){continue;} xLen = ((localStorage[x].length * 2 + x.length * 2)/1024); log.push(x.substr(0,30) + " = " + xLen.toFixed(2) + " KB"); total+= xLen}; if (total > 1024){log.unshift("Total = " + (total/1024).toFixed(2)+ " MB");}else{log.unshift("Total = " + total.toFixed(2)+ " KB");}; alert(log.join("\n"));
P.S. Snippets are updated according to request in the comment. Now the calculation includes the length of the key itself.
Each length is multiplied by 2 because the char in javascript stores as UTF-16 (occupies 2 bytes)
P.P.S. Should work both in Chrome and Firefox.
Going off of what #Shourav said above, I wrote a small function that should accurately grab all your the localStorage keys (for the current domain) and calculate the combined size so that you know exactly how much memory is taken up by your localStorage object:
var localStorageSpace = function(){
var allStrings = '';
for(var key in window.localStorage){
if(window.localStorage.hasOwnProperty(key)){
allStrings += window.localStorage[key];
}
}
return allStrings ? 3 + ((allStrings.length*16)/(8*1024)) + ' KB' : 'Empty (0 KB)';
};
Mine returned: "30.896484375 KB"
You can get the current size of the local storage data using the Blob function. This may not work in old browsers, check the support for new Blob and Object.values() at caniuse.
Example:
return new Blob(Object.values(localStorage)).size;
Object.values() turns the localStorage object to an array. Blob turns the array into raw data.
IE has a remainingSpace property of the Storage object. The other browsers have no equivalent at this time.
I believe that the default amount of space is 5MB, although I have not tested it personally.
Here is a simple example of how to do this and should work with every browser
alert(1024 * 1024 * 5 - unescape(encodeURIComponent(JSON.stringify(localStorage))).length);
Hope this help someone.
Because Jas- example on jsfiddle does not work for me I came up with this solution.
(thanks to Serge Seletskyy and Shourav for their bits I used in the code below)
Below is the function that can be used to test how much space is available for localStorage and (if any keys are already in lS) how much space is left.
It is a little brute force but it works in almost every browser... apart from Firefox.
Well in desktop FF it takes ages (4-5min) to complete, and on Android it just crashes.
Underneath the function is a short summary of tests that I have done in different browsers on different platforms. Enjoy!
function testLocalStorage() {
var timeStart = Date.now();
var timeEnd, countKey, countValue, amountLeft, itemLength;
var occupied = leftCount = 3; //Shurav's comment on initial overhead
//create localStorage entries until localStorage is totally filled and browser issues a warning.
var i = 0;
while (!error) {
try {
//length of the 'value' was picked to be a compromise between speed and accuracy,
// the longer the 'value' the quicker script and result less accurate. This one is around 2Kb
localStorage.setItem('testKey' + i, '11111111112222222222333333333344444444445555555555666661111111111222222222233333333334444444444555555555566666');
} catch (e) {
var error = e;
}
i++;
}
//if the warning was issued - localStorage is full.
if (error) {
//iterate through all keys and values to count their length
for (var i = 0; i < localStorage.length; i++) {
countKey = localStorage.key(i);
countValue = localStorage.getItem(localStorage.key(i));
itemLength = countKey.length + countValue.length;
//if the key is one of our 'test' keys count it separately
if (countKey.indexOf("testKey") !== -1) {
leftCount = leftCount + itemLength;
}
//count all keys and their values
occupied = occupied + itemLength;
}
;
//all keys + values lenght recalculated to Mb
occupied = (((occupied * 16) / (8 * 1024)) / 1024).toFixed(2);
//if there are any other keys then our 'testKeys' it will show how much localStorage is left
amountLeft = occupied - (((leftCount * 16) / (8 * 1024)) / 1024).toFixed(2);
//iterate through all localStorage keys and remove 'testKeys'
Object.keys(localStorage).forEach(function(key) {
if (key.indexOf("testKey") !== -1) {
localStorage.removeItem(key);
}
});
}
//calculate execution time
var timeEnd = Date.now();
var time = timeEnd - timeStart;
//create message
var message = 'Finished in: ' + time + 'ms \n total localStorage: ' + occupied + 'Mb \n localStorage left: ' + amountLeft + "Mb";
//put the message on the screen
document.getElementById('scene').innerText = message; //this works with Chrome,Safari, Opera, IE
//document.getElementById('scene').textContent = message; //Required for Firefox to show messages
}
And as promised above some test in different browsers:
GalaxyTab 10.1
Maxthon Pad 1.7 ~1130ms 5Mb
Firefox 20.0(Beta 20.0) crashed both
Chrome 25.0.1364.169 ~22250ms /5Mb
Native (identifies as Safari 4.0/Webkit534.30) ~995ms /5Mb
iPhone 4s iOS 6.1.3
Safari ~ 520ms /5Mb
As HomeApp ~525ms / 5Mb
iCab ~ 710ms /5mb
MacBook Pro OSX 1.8.3 (Core 2 Duo 2.66 8Gb memory)
Safari 6.0.3 ~105ms /5Mb
Chrome 26.0.1410.43 ~3400ms /5Mb
Firefox 20.0 300150ms(!) /10Mb (after complaining about script running to long)
iPad 3 iOS 6.1.3
Safari ~430ms /5Mb
iCab ~595ms /5mb
Windows 7 -64b (Core 2 Duo 2.93 6Gb memory)
Safari 5.1.7 ~80ms /5Mb
Chrome 26.0.1410.43 ~1220ms /5Mb
Firefox 20.0 228500ms(!) /10Mb (after complaining about script running to long)
IE9 ~17900ms /9.54Mb ( if any console.logs are in the code does not work until DevTools are opened)
Opera 12.15 ~4212ms /3.55Mb (this is when 5Mb is selected, but Opera asks nicely if we want increase the amount of lS, unfortunately it crashes if test conducted a few times in a row)
Win 8 (Under Parallels 8)
IE10 ~7850ms /9.54Mb
You can calculate your localstorage by following methods:
function sizeofAllStorage(){ // provide the size in bytes of the data currently stored
var size = 0;
for (i=0; i<=localStorage.length-1; i++)
{
key = localStorage.key(i);
size += lengthInUtf8Bytes(localStorage.getItem(key));
}
return size;
}
function lengthInUtf8Bytes(str) {
// Matches only the 10.. bytes that are non-initial characters in a multi-byte sequence.
var m = encodeURIComponent(str).match(/%[89ABab]/g);
return str.length + (m ? m.length : 0);
}
console.log(sizeofAllStorage());
Finally size in bytes will be logged in browser.
I would use the code of #tennisgen which get all and count the content, but I count the keys themselves:
var localStorageSpace = function(){
var allStrings = '';
for(var key in window.localStorage){
allStrings += key;
if(window.localStorage.hasOwnProperty(key)){
allStrings += window.localStorage[key];
}
}
return allStrings ? 3 + ((allStrings.length*16)/(8*1024)) + ' KB' : 'Empty (0 KB)';
};
The way I went about this problem is to create functions for finding out the used space and remaining space in Local Storage and then a function that calls those functions to determine the max storage space.
function getUsedSpaceOfLocalStorageInBytes() {
// Returns the total number of used space (in Bytes) of the Local Storage
var b = 0;
for (var key in window.localStorage) {
if (window.localStorage.hasOwnProperty(key)) {
b += key.length + localStorage.getItem(key).length;
}
}
return b;
}
function getUnusedSpaceOfLocalStorageInBytes() {
var maxByteSize = 10485760; // 10MB
var minByteSize = 0;
var tryByteSize = 0;
var testQuotaKey = 'testQuota';
var timeout = 20000;
var startTime = new Date().getTime();
var unusedSpace = 0;
do {
runtime = new Date().getTime() - startTime;
try {
tryByteSize = Math.floor((maxByteSize + minByteSize) / 2);
//localStorage.setItem(testQuotaKey, new Array(tryByteSize).join('1'));
//Recommended by #pkExec and #jrob007
localStorage.setItem(testQuotaKey, String('1').repeat(tryByteSize));
minByteSize = tryByteSize;
} catch (e) {
maxByteSize = tryByteSize - 1;
}
} while ((maxByteSize - minByteSize > 1) && runtime < timeout);
localStorage.removeItem(testQuotaKey);
if (runtime >= timeout) {
console.log("Unused space calculation may be off due to timeout.");
}
// Compensate for the byte size of the key that was used, then subtract 1 byte because the last value of the tryByteSize threw the exception
unusedSpace = tryByteSize + testQuotaKey.length - 1;
return unusedSpace;
}
function getLocalStorageQuotaInBytes() {
// Returns the total Bytes of Local Storage Space that the browser supports
var unused = getUnusedSpaceOfLocalStorageInBytes();
var used = getUsedSpaceOfLocalStorageInBytes();
var quota = unused + used;
return quota;
}
In addition to #serge's answer which is most voted here, size of the keys need to be considered. Code below will add the size of the keys stored in localStorage
var t = 0;
for (var x in localStorage) {
t += (x.length + localStorage[x].length) * 2;
}
console.log((t / 1024) + " KB");
As the spec goes, each character of a string is 16 bit.
But inspecting with chrome (Settings>Content Settings>Cookies & Site data) shows us that initiating localStorage takes 3kB (overhead size)
And stored data size follows this relation (accurate to 1kB)
3 + ((localStorage.x.length*16)/(8*1024)) kB
where localStorage.x is your storage string.
Yes, this question was asked like 10 years ago. But for those interested (like myself, as I am building an offline text editor that saves data with local storage) and suck at programming, you could use something simple like this:
var warning = 1;
var limit = 2000000; //2 million characters, not really taking in account to bytes but for tested number of characters stored
setInterval(function() {
localStorage["text"] = document.getElementById("editor").innerHTML; //gets text and saves it in local storage under "text"
if(localStorage["text"].length > limit && warning == 1){
alert("Local Storage capacity has been filled");
warning = 2; //prevent a stream of alerts
}
}, 1000);
//setInterval function saves and checks local storage
The best way to get the amount of storage filled is to view the site settings (say, if you stored an image in local storage). At least in chrome, you can see the amount of bytes used (ie: 1222 bytes). However, the best ways to see filled local storage with js have already been mentioned above, so use them.
//Memory occupy by both key and value so Updated Code.
var jsonarr=[];
var jobj=null;
for(x in sessionStorage) // Iterate through each session key
{
jobj={};
jobj[x]=sessionStorage.getItem(x); //because key will also occupy some memory
jsonarr.push(jobj);
jobj=null;
}
//https://developer.mozilla.org/en/docs/Web/JavaScript/Data_structures
//JavaScript's String type is used to represent textual data. It is a set of "elements" of 16-bit unsigned integer values.
var size=JSON.stringify(jsonarr).length*2; //16-bit that's why multiply by 2
var arr=["bytes","KB","MB","GB","TB"]; // Define Units
var sizeUnit=0;
while(size>1024){ // To get result in Proper Unit
sizeUnit++;
size/=1024;
}
alert(size.toFixed(2)+" "+arr[sizeUnit]);
window.localStorage.remainingSpace
Since localStorage (currently) only supports strings as values, and in order to do that the objects need to be stringified (stored as JSON-string) before they can be stored, is there a defined limitation regarding the length of the values.
Does anyone know if there is a definition which applies to all browsers?
Quoting from the Wikipedia article on Web Storage:
Web storage can be viewed simplistically as an improvement on cookies, providing much greater storage capacity (10 MB per origin in Google Chrome(https://plus.google.com/u/0/+FrancoisBeaufort/posts/S5Q9HqDB8bh), Mozilla Firefox, and Opera; 10 MB per storage area in Internet Explorer) and better programmatic interfaces.
And also quoting from a John Resig article [posted January 2007]:
Storage Space
It is implied that, with DOM Storage,
you have considerably more storage
space than the typical user agent
limitations imposed upon Cookies.
However, the amount that is provided
is not defined in the specification,
nor is it meaningfully broadcast by
the user agent.
If you look at the Mozilla source code
we can see that 5120KB is the default
storage size for an entire domain.
This gives you considerably more space
to work with than a typical 2KB
cookie.
However, the size of this storage area
can be customized by the user (so a
5MB storage area is not guaranteed,
nor is it implied) and the user agent
(Opera, for example, may only provide
3MB - but only time will tell.)
Actually Opera doesn't have 5MB limit. It offers to increase limit as applications requires more. User can even choose "Unlimited storage" for a domain.
You can easily test localStorage limits/quota yourself.
Here's a straightforward script for finding out the limit:
if (localStorage && !localStorage.getItem('size')) {
var i = 0;
try {
// Test up to 10 MB
for (i = 250; i <= 10000; i += 250) {
localStorage.setItem('test', new Array((i * 1024) + 1).join('a'));
}
} catch (e) {
localStorage.removeItem('test');
localStorage.setItem('size', i - 250);
}
}
Here's the gist, JSFiddle and blog post.
The script will test setting increasingly larger strings of text until the browser throws and exception. At that point it’ll clear out the test data and set a size key in localStorage storing the size in kilobytes.
Find the maximum length of a single string that can be stored in localStorage
This snippet will find the maximum length of a String that can be stored in localStorage per domain.
//Clear localStorage
for (var item in localStorage) delete localStorage[item];
window.result = window.result || document.getElementById('result');
result.textContent = 'Test running…';
//Start test
//Defer running so DOM can be updated with "test running" message
setTimeout(function () {
//Variables
var low = 0,
high = 2e9,
half;
//Two billion may be a little low as a starting point, so increase if necessary
while (canStore(high)) high *= 2;
//Keep refining until low and high are equal
while (low !== high) {
half = Math.floor((high - low) / 2 + low);
//Check if we can't scale down any further
if (low === half || high === half) {
console.info(low, high, half);
//Set low to the maximum possible amount that can be stored
low = canStore(high) ? high : low;
high = low;
break;
}
//Check if the maximum storage is no higher than half
if (storageMaxBetween(low, half)) {
high = half;
//The only other possibility is that it's higher than half but not higher than "high"
} else {
low = half + 1;
}
}
//Show the result we found!
result.innerHTML = 'The maximum length of a string that can be stored in localStorage is <strong>' + low + '</strong> characters.';
//Functions
function canStore(strLen) {
try {
delete localStorage.foo;
localStorage.foo = Array(strLen + 1).join('A');
return true;
} catch (ex) {
return false;
}
}
function storageMaxBetween(low, high) {
return canStore(low) && !canStore(high);
}
}, 0);
<h1>LocalStorage single value max length test</h1>
<div id='result'>Please enable JavaScript</div>
Note that the length of a string is limited in JavaScript; if you want to view the maximum amount of data that can be stored in localStorage when not limited to a single string, you can use the code in this answer.
Edit: Stack Snippets don't support localStorage, so here is a link to JSFiddle.
Results
Chrome (45.0.2454.101): 5242878 characters
Firefox (40.0.1): 5242883 characters
Internet Explorer (11.0.9600.18036): 16386 122066 122070 characters
I get different results on each run in Internet Explorer.
Don't assume 5MB is available - localStorage capacity varies by browser, with 2.5MB, 5MB and unlimited being the most common values.
Source: http://dev-test.nemikor.com/web-storage/support-test/
I wrote this simple code that is testing localStorage size in bytes.
https://github.com/gkucmierz/Test-of-localStorage-limits-quota
const check = bytes => {
try {
localStorage.clear();
localStorage.setItem('a', '0'.repeat(bytes));
localStorage.clear();
return true;
} catch(e) {
localStorage.clear();
return false;
}
};
Github pages:
https://gkucmierz.github.io/Test-of-localStorage-limits-quota/
I have the same results on desktop Google chrome, opera, firefox, brave and mobile chrome which is ~10Mbytes
And half smaller result in safari ~4Mbytes
You don't want to stringify large objects into a single localStorage entry. That would be very inefficient - the whole thing would have to be parsed and re-encoded every time some slight detail changes. Also, JSON can't handle multiple cross references within an object structure and wipes out a lot of details, e.g. the constructor, non-numerical properties of arrays, what's in a sparse entry, etc.
Instead, you can use Rhaboo. It stores large objects using lots of localStorage entries so you can make small changes quickly. The restored objects are much more accurate copies of the saved ones and the API is incredibly simple. E.g.:
var store = Rhaboo.persistent('Some name');
store.write('count', store.count ? store.count+1 : 1);
store.write('somethingfancy', {
one: ['man', 'went'],
2: 'mow',
went: [ 2, { mow: ['a', 'meadow' ] }, {} ]
});
store.somethingfancy.went[1].mow.write(1, 'lawn');
BTW, I wrote it.
I've condensed a binary test into this function that I use:
function getStorageTotalSize(upperLimit/*in bytes*/) {
var store = localStorage, testkey = "$_test"; // (NOTE: Test key is part of the storage!!! It should also be an even number of characters)
var test = function (_size) { try { store.removeItem(testkey); store.setItem(testkey, new Array(_size + 1).join('0')); } catch (_ex) { return false; } return true; }
var backup = {};
for (var i = 0, n = store.length; i < n; ++i) backup[store.key(i)] = store.getItem(store.key(i));
store.clear(); // (you could iterate over the items and backup first then restore later)
var low = 0, high = 1, _upperLimit = (upperLimit || 1024 * 1024 * 1024) / 2, upperTest = true;
while ((upperTest = test(high)) && high < _upperLimit) { low = high; high *= 2; }
if (!upperTest) {
var half = ~~((high - low + 1) / 2); // (~~ is a faster Math.floor())
high -= half;
while (half > 0) high += (half = ~~(half / 2)) * (test(high) ? 1 : -1);
high = testkey.length + high;
}
if (high > _upperLimit) high = _upperLimit;
store.removeItem(testkey);
for (var p in backup) store.setItem(p, backup[p]);
return high * 2; // (*2 because of Unicode storage)
}
It also backs up the contents before testing, then restores them.
How it works: It doubles the size until the limit is reached or the test fails. It then stores half the distance between low and high and subtracts/adds a half of the half each time (subtract on failure and add on success); honing into the proper value.
upperLimit is 1GB by default, and just limits how far upwards to scan exponentially before starting the binary search. I doubt this will even need to be changed, but I'm always thinking ahead. ;)
On Chrome:
> getStorageTotalSize();
> 10485762
> 10485762/2
> 5242881
> localStorage.setItem("a", new Array(5242880).join("0")) // works
> localStorage.setItem("a", new Array(5242881).join("0")) // fails ('a' takes one spot [2 bytes])
IE11, Edge, and FireFox also report the same max size (10485762 bytes).
You can use the following code in modern browsers to efficiently check the storage quota (total & used) in real-time:
if ('storage' in navigator && 'estimate' in navigator.storage) {
navigator.storage.estimate()
.then(estimate => {
console.log("Usage (in Bytes): ", estimate.usage,
", Total Quota (in Bytes): ", estimate.quota);
});
}
I'm doing the following:
getLocalStorageSizeLimit = function () {
var maxLength = Math.pow(2,24);
var preLength = 0;
var hugeString = "0";
var testString;
var keyName = "testingLengthKey";
//2^24 = 16777216 should be enough to all browsers
testString = (new Array(Math.pow(2, 24))).join("X");
while (maxLength !== preLength) {
try {
localStorage.setItem(keyName, testString);
preLength = testString.length;
maxLength = Math.ceil(preLength + ((hugeString.length - preLength) / 2));
testString = hugeString.substr(0, maxLength);
} catch (e) {
hugeString = testString;
maxLength = Math.floor(testString.length - (testString.length - preLength) / 2);
testString = hugeString.substr(0, maxLength);
}
}
localStorage.removeItem(keyName);
// Original used this.storageObject in place of localStorage. I can only guess the goal is to check the size of the localStorage with everything but the testString given that maxLength is then added.
maxLength = JSON.stringify(localStorage).length + maxLength + keyName.length - 2;
return maxLength;
};
I really like cdmckay's answer, but it does not really look good to check the size in a real time: it is just too slow (2 seconds for me). This is the improved version, which is way faster and more exact, also with an option to choose how big the error can be (default 250,000, the smaller error is - the longer the calculation is):
function getLocalStorageMaxSize(error) {
if (localStorage) {
var max = 10 * 1024 * 1024,
i = 64,
string1024 = '',
string = '',
// generate a random key
testKey = 'size-test-' + Math.random().toString(),
minimalFound = 0,
error = error || 25e4;
// fill a string with 1024 symbols / bytes
while (i--) string1024 += 1e16;
i = max / 1024;
// fill a string with 'max' amount of symbols / bytes
while (i--) string += string1024;
i = max;
// binary search implementation
while (i > 1) {
try {
localStorage.setItem(testKey, string.substr(0, i));
localStorage.removeItem(testKey);
if (minimalFound < i - error) {
minimalFound = i;
i = i * 1.5;
}
else break;
} catch (e) {
localStorage.removeItem(testKey);
i = minimalFound + (i - minimalFound) / 2;
}
}
return minimalFound;
}
}
To test:
console.log(getLocalStorageMaxSize()); // takes .3s
console.log(getLocalStorageMaxSize(.1)); // takes 2s, but way more exact
This works dramatically faster for the standard error; also it can be much more exact when necessary.
Once I developed Chrome (desktop browser) extension and tested Local Storage real max size for this reason.
My results:
Ubuntu 18.04.1 LTS (64-bit)
Chrome 71.0.3578.98 (Official Build) (64-bit)
Local Storage content size 10240 KB (10 MB)
More than 10240 KB usage returned me the error:
Uncaught DOMException: Failed to execute 'setItem' on 'Storage': Setting the value of 'notes' exceeded the quota.
Edit on Oct 23, 2020
For a Chrome extensions available chrome.storage API. If you declare the "storage" permission in manifest.js:
{
"name": "My extension",
...
"permissions": ["storage"],
...
}
You can access it like this:
chrome.storage.local.QUOTA_BYTES // 5242880 (in bytes)
I've got a web page that uses XMLHttpRequest to download a binary resource.
In Firefox and Gecko I can use responseText to get the bytes, even if the bytestream includes binary zeroes. I may need to coerce the mimetype with overrideMimeType() to make that happen. In IE, though, responseText doesn't work, because it appears to terminate at the first zero. If you read 100,000 bytes, and byte 7 is a binary zero, you will be able to access only 7 bytes. IE's XMLHttpRequest exposes a responseBody property to access the bytes. I've seen a few posts suggesting that it's impossible to access this property in any meaningful way directly from Javascript. This sounds crazy to me.
xhr.responseBody is accessible from VBScript, so the obvious workaround is to define a method in VBScript in the webpage, and then call that method from Javascript. See jsdap for one example. EDIT: DO NOT USE THIS VBScript!!
var IE_HACK = (/msie/i.test(navigator.userAgent) &&
!/opera/i.test(navigator.userAgent));
// no no no! Don't do this!
if (IE_HACK) document.write('<script type="text/vbscript">\n\
Function BinaryToArray(Binary)\n\
Dim i\n\
ReDim byteArray(LenB(Binary))\n\
For i = 1 To LenB(Binary)\n\
byteArray(i-1) = AscB(MidB(Binary, i, 1))\n\
Next\n\
BinaryToArray = byteArray\n\
End Function\n\
</script>');
var xml = (window.XMLHttpRequest)
? new XMLHttpRequest() // Mozilla/Safari/IE7+
: (window.ActiveXObject)
? new ActiveXObject("MSXML2.XMLHTTP") // IE6
: null; // Commodore 64?
xml.open("GET", url, true);
if (xml.overrideMimeType) {
xml.overrideMimeType('text/plain; charset=x-user-defined');
} else {
xml.setRequestHeader('Accept-Charset', 'x-user-defined');
}
xml.onreadystatechange = function() {
if (xml.readyState == 4) {
if (!binary) {
callback(xml.responseText);
} else if (IE_HACK) {
// call a VBScript method to copy every single byte
callback(BinaryToArray(xml.responseBody).toArray());
} else {
callback(getBuffer(xml.responseText));
}
}
};
xml.send('');
Is this really true? The best way? copying every byte? For a large binary stream that's not going to be very efficient.
There is also a possible technique using ADODB.Stream, which is a COM equivalent of a MemoryStream. See here for an example. It does not require VBScript but does require a separate COM object.
if (typeof (ActiveXObject) != "undefined" && typeof (httpRequest.responseBody) != "undefined") {
// Convert httpRequest.responseBody byte stream to shift_jis encoded string
var stream = new ActiveXObject("ADODB.Stream");
stream.Type = 1; // adTypeBinary
stream.Open ();
stream.Write (httpRequest.responseBody);
stream.Position = 0;
stream.Type = 1; // adTypeBinary;
stream.Read.... /// ???? what here
}
But that's not going to work well - ADODB.Stream is disabled on most machines these days.
In The IE8 developer tools - the IE equivalent of Firebug - I can see the responseBody is an array of bytes and I can even see the bytes themselves. The data is right there. I don't understand why I can't get to it.
Is it possible for me to read it with responseText?
hints? (other than defining a VBScript method)
Yes, the answer I came up with for reading binary data via XHR in IE, is to use VBScript injection. This was distasteful to me at first, but, I look at it as just one more browser dependent bit of code.
(The regular XHR and responseText works fine in other browsers; you may have to coerce the mime type with XMLHttpRequest.overrideMimeType(). This isn't available on IE).
This is how I got a thing that works like responseText in IE, even for binary data.
First, inject some VBScript as a one-time thing, like this:
if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
var IEBinaryToArray_ByteStr_Script =
"<!-- IEBinaryToArray_ByteStr -->\r\n"+
"<script type='text/vbscript' language='VBScript'>\r\n"+
"Function IEBinaryToArray_ByteStr(Binary)\r\n"+
" IEBinaryToArray_ByteStr = CStr(Binary)\r\n"+
"End Function\r\n"+
"Function IEBinaryToArray_ByteStr_Last(Binary)\r\n"+
" Dim lastIndex\r\n"+
" lastIndex = LenB(Binary)\r\n"+
" if lastIndex mod 2 Then\r\n"+
" IEBinaryToArray_ByteStr_Last = Chr( AscB( MidB( Binary, lastIndex, 1 ) ) )\r\n"+
" Else\r\n"+
" IEBinaryToArray_ByteStr_Last = "+'""'+"\r\n"+
" End If\r\n"+
"End Function\r\n"+
"</script>\r\n";
// inject VBScript
document.write(IEBinaryToArray_ByteStr_Script);
}
The JS class I'm using that reads binary files exposes a single interesting method, readCharAt(i), which reads the character (a byte, really) at the i'th index. This is how I set it up:
// see doc on http://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx
function getXMLHttpRequest()
{
if (window.XMLHttpRequest) {
return new window.XMLHttpRequest;
}
else {
try {
return new ActiveXObject("MSXML2.XMLHTTP");
}
catch(ex) {
return null;
}
}
}
// this fn is invoked if IE
function IeBinFileReaderImpl(fileURL){
this.req = getXMLHttpRequest();
this.req.open("GET", fileURL, true);
this.req.setRequestHeader("Accept-Charset", "x-user-defined");
// my helper to convert from responseBody to a "responseText" like thing
var convertResponseBodyToText = function (binary) {
var byteMapping = {};
for ( var i = 0; i < 256; i++ ) {
for ( var j = 0; j < 256; j++ ) {
byteMapping[ String.fromCharCode( i + j * 256 ) ] =
String.fromCharCode(i) + String.fromCharCode(j);
}
}
// call into VBScript utility fns
var rawBytes = IEBinaryToArray_ByteStr(binary);
var lastChr = IEBinaryToArray_ByteStr_Last(binary);
return rawBytes.replace(/[\s\S]/g,
function( match ) { return byteMapping[match]; }) + lastChr;
};
this.req.onreadystatechange = function(event){
if (that.req.readyState == 4) {
that.status = "Status: " + that.req.status;
//that.httpStatus = that.req.status;
if (that.req.status == 200) {
// this doesn't work
//fileContents = that.req.responseBody.toArray();
// this doesn't work
//fileContents = new VBArray(that.req.responseBody).toArray();
// this works...
var fileContents = convertResponseBodyToText(that.req.responseBody);
fileSize = fileContents.length-1;
if(that.fileSize < 0) throwException(_exception.FileLoadFailed);
that.readByteAt = function(i){
return fileContents.charCodeAt(i) & 0xff;
};
}
if (typeof callback == "function"){ callback(that);}
}
};
this.req.send();
}
// this fn is invoked if non IE
function NormalBinFileReaderImpl(fileURL){
this.req = new XMLHttpRequest();
this.req.open('GET', fileURL, true);
this.req.onreadystatechange = function(aEvt) {
if (that.req.readyState == 4) {
if(that.req.status == 200){
var fileContents = that.req.responseText;
fileSize = fileContents.length;
that.readByteAt = function(i){
return fileContents.charCodeAt(i) & 0xff;
}
if (typeof callback == "function"){ callback(that);}
}
else
throwException(_exception.FileLoadFailed);
}
};
//XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com]
this.req.overrideMimeType('text/plain; charset=x-user-defined');
this.req.send(null);
}
The conversion code was provided by Miskun.
Very fast, works great.
I used this method to read and extract zip files from Javascript, and also in a class that reads and displays EPUB files in Javascript. Very reasonable performance. About half a second for a 500kb file.
XMLHttpRequest.responseBody is a VBArray object containing the raw bytes. You can convert these objects to standard arrays using the toArray() function:
var data = xhr.responseBody.toArray();
I would suggest two other (fast) options:
First, you can use
ADODB.Recordset to convert the byte array into a string. I would guess that this object is more common that ADODB.Stream, which is often disabled for security reasons. This option is VERY fast, less than 30ms for a 500kB file.
Second, if the Recordset component is not accessible, there is a trick to access the byte array data from Javascript. Send your xhr.responseBody to VBScript, pass it through any VBScript string function such as CStr (takes no time), and return it to JS. You will get a weird string with bytes concatenated into 16-bit unicode (in reverse). You can then convert this string quickly into a usable bytestring through a regular expression with dictionary-based replacement. Takes about 1s for 500kB.
For comparison, the byte-by-byte conversion through loops takes several minutes for this same 500kB file, so it's a no-brainer :) Below the code I have been using, to insert into your header. Then call the function ieGetBytes with your xhr.responseBody.
<!--[if IE]>
<script type="text/vbscript">
'Best case scenario when the ADODB.Recordset object exists
'We will do the existence test in Javascript (see after)
'Extremely fast, about 25ms for a 500kB file
Function ieGetBytesADO(byteArray)
Dim recordset
Set recordset = CreateObject("ADODB.Recordset")
With recordset
.Fields.Append "temp", 201, LenB(byteArray)
.Open
.AddNew
.Fields("temp").AppendChunk byteArray
.Update
End With
ieGetBytesADO = recordset("temp")
recordset.Close
Set recordset = Nothing
End Function
'Trick to return a Javascript-readable string from a VBScript byte array
'Yet the string is not usable as such by Javascript, since the bytes
'are merged into 16-bit unicode characters. Last character missing if odd length.
Function ieRawBytes(byteArray)
ieRawBytes = CStr(byteArray)
End Function
'Careful the last character is missing in case of odd file length
'We Will call the ieLastByte function (below) from Javascript
'Cannot merge directly within ieRawBytes as the final byte would be duplicated
Function ieLastChr(byteArray)
Dim lastIndex
lastIndex = LenB(byteArray)
if lastIndex mod 2 Then
ieLastChr = Chr( AscB( MidB( byteArray, lastIndex, 1 ) ) )
Else
ieLastChr = ""
End If
End Function
</script>
<script type="text/javascript">
try {
// best case scenario, the ADODB.Recordset object exists
// we can use the VBScript ieGetBytes function to transform a byte array into a string
var ieRecordset = new ActiveXObject('ADODB.Recordset');
var ieGetBytes = function( byteArray ) {
return ieGetBytesADO(byteArray);
}
ieRecordset = null;
} catch(err) {
// no ADODB.Recordset object, we will do the conversion quickly through a regular expression
// initializes for once and for all the translation dictionary to speed up our regexp replacement function
var ieByteMapping = {};
for ( var i = 0; i < 256; i++ ) {
for ( var j = 0; j < 256; j++ ) {
ieByteMapping[ String.fromCharCode( i + j * 256 ) ] = String.fromCharCode(i) + String.fromCharCode(j);
}
}
// since ADODB is not there, we replace the previous VBScript ieGetBytesADO function with a regExp-based function,
// quite fast, about 1.3 seconds for 500kB (versus several minutes for byte-by-byte loops over the byte array)
var ieGetBytes = function( byteArray ) {
var rawBytes = ieRawBytes(byteArray),
lastChr = ieLastChr(byteArray);
return rawBytes.replace(/[\s\S]/g, function( match ) {
return ieByteMapping[match]; }) + lastChr;
}
}
</script>
<![endif]-->
Thanks so much for this solution. the BinaryToArray() function in VbScript works great for me.
Incidentally, I need the binary data for providing it to an Applet. (Don't ask me why Applets can't be used for downloading binary data. Long story short.. weird MS authentication that cant go thru applets (URLConn) calls. Its especially weird in cases where users are behind a proxy )
The Applet needs a byte array from this data, so here's what I do to get it:
String[] results = result.toString().split(",");
byte[] byteResults = new byte[results.length];
for (int i=0; i<results.length; i++){
byteResults[i] = (byte)Integer.parseInt(results[i]);
}
The byte array can then converted into a bytearrayinputstream for further processing.
Thank you for this post.
I found this link usefull:
http://www.codingforums.com/javascript-programming/47018-help-using-responsetext-property-microsofts-xmlhttp-activexobject-ie6.html
Specially this part:
</script>
<script language="VBScript">
Function BinaryToString(Binary)
Dim I,S
For I = 1 to LenB(Binary)
S = S & Chr(AscB(MidB(Binary,I,1)))
Next
BinaryToString = S
End Function
</script>
I've added this to my htm page.
Then I call this function from my javascript:
responseText = BinaryToString(xhr.responseBody);
Works on IE8, IE9, IE10, FF & Chrome.
You could also just make a proxy script that goes to the address you're requesting & base64's it. Then you just have to pass a query string to the proxy script that tells it the address. In IE you have to manually do base64 in JS though. But this is a way to go if you don't want to use VBScript.
I used this for my GameBoy Color emulator.
Here is the PHP script that does the magic:
<?php
//Binary Proxy
if (isset($_GET['url'])) {
try {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, stripslashes($_GET['url']));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($curl, CURLOPT_POST, false);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 30);
$result = curl_exec($curl);
curl_close($curl);
if ($result !== false) {
header('Content-Type: text/plain; charset=ASCII');
header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T', time() + (3600 * 24 * 7)));
echo(base64_encode($result));
}
else {
header('HTTP/1.0 404 File Not Found');
}
}
catch (Exception $error) { }
}
?>
I was trying to download a file and than sign it using CAPICOM.DLL. The only way I coud do it was by injecting a VBScript function that does the download. That is my solution:
if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) {
var VBConteudo_Script =
'<!-- VBConteudo -->\r\n'+
'<script type="text/vbscript">\r\n'+
'Function VBConteudo(url)\r\n'+
' Set objHTTP = CreateObject("MSXML2.XMLHTTP")\r\n'+
' objHTTP.open "GET", url, False\r\n'+
' objHTTP.send\r\n'+
' If objHTTP.Status = 200 Then\r\n'+
' VBConteudo = objHTTP.responseBody\r\n'+
' End If\r\n'+
'End Function\r\n'+
'\<\/script>\r\n';
// inject VBScript
document.write(VBConteudo_Script);
}