Mozilla

Saving images and files in localStorage

As you might know, localStorage is quite powerful when it comes to quickly storing information in the user’s web browser, and it has also been around in all web browsers a long time. But how do we store files in it?

Please also make sure to read Storing images and files in IndexedDB.

Using JSON for powerful control

Let’s start with some basic information about localStorage. When storing information in localStorage, you use key/value pairs, like this:

localStorage.setItem("name", "Robert");

And to read it out, you just get the value of that key:

localStorage.getItem("name");

That’s all good and well, and being able to save at least 5 MB, you have a lot of options. But since localStorage is just string-based, saving a long string with no form of structure isn’t optimal.
Therefore, we can utilize the native JSON support in web browsers to turn JavaScript objects to string for saving, and back into objects when reading:


var cast = {
    "Adm. Adama" : "Edward James Olmos",
    "President Roslin" : "Mary McDonnell",
    "Captain Adama" : "Jamie Bamber",
    "Gaius Baltar" : "James Callis",
    "Number Six" : "Tricia Helfer",
    "Kara Thrace" : "	Katee Sackhoff"
};

// Stores the JavaScript object as a string
localStorage.setItem("cast", JSON.stringify(cast));

// Parses the saved string into a JavaScript object again 
JSON.parse(localStorage.getItem("cast"));

Storing images

The idea here is to be able to take an image that has been loaded into the current web page and store it into localStorage. As we established above, localStorage only supports strings, so what we need to do here is turn the image into a Data URL. One way to do this for an image, is to load into a canvas element. Then, with a canvas, you can read out the current visual representation in a canvas as a Data URL.

Let’s look at this example where we have an image in the document with an id of “elephant”:


// Get a reference to the image element
var elephant = document.getElementById("elephant");

// Take action when the image has loaded
elephant.addEventListener("load", function () {
    var imgCanvas = document.createElement("canvas"),
        imgContext = imgCanvas.getContext("2d");

    // Make sure canvas is as big as the picture
    imgCanvas.width = elephant.width;
    imgCanvas.height = elephant.height;

    // Draw image into canvas element
    imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);

    // Get canvas contents as a data URL
    var imgAsDataURL = imgCanvas.toDataURL("image/png");

    // Save image into localStorage
    localStorage.setItem("elephant", imgAsDataURL);
}, false);

Then, if we want to take it further, we can utilize a JavaScript object and do a date check with localStorage. In this example, we load the image from the server through JavaScript the first time, but for every page load after that, we read the saved image from localStorage instead:

HTML


<figure>
    <img id="elephant" src="about:blank" alt="A close up of an elephant">
    <noscript>
        <img src="elephant.png" alt="A close up of an elephant">
    </noscript>    
    <figcaption>A mighty big elephant, and mighty close too!</figcaption>
</figure>

JavaScript


// localStorage with image
var storageFiles = JSON.parse(localStorage.getItem("storageFiles")) || {},
    elephant = document.getElementById("elephant"),
    storageFilesDate = storageFiles.date,
    date = new Date(),
    todaysDate = (date.getMonth() + 1).toString() + date.getDate().toString();

// Compare date and create localStorage if it's not existing/too old
if (typeof storageFilesDate === "undefined" || storageFilesDate < todaysDate) {
    // Take action when the image has loaded
    elephant.addEventListener("load", function () {
        var imgCanvas = document.createElement("canvas"),
            imgContext = imgCanvas.getContext("2d");

        // Make sure canvas is as big as the picture
        imgCanvas.width = elephant.width;
        imgCanvas.height = elephant.height;

        // Draw image into canvas element
        imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);

        // Save image as a data URL
        storageFiles.elephant = imgCanvas.toDataURL("image/png");

        // Set date for localStorage
        storageFiles.date = todaysDate;

        // Save as JSON in localStorage
        localStorage.setItem("storageFiles", JSON.stringify(storageFiles));
    }, false);

    // Set initial image src
    elephant.setAttribute("src", "elephant.png");
}
else {
    // Use image from localStorage
    elephant.setAttribute("src", storageFiles.elephant);
}

Note: A word of warning here is that you might exceed the size available in localStorage, and the best way to control that is using try...catch.

Storing any kind of file

So thats great, we can use canvas to turn images into Data URL and save in localStorage. But what if we want a mechanism that works for any kind of file?

Then it becomes a bit interesting, and we will need to use:

The basic approach is:

  1. Do an XMLHttpRequest for a file, and set the responseType to “arraybuffer”
  2. Load the response, i.e. file, of the XMLHttpRequest into a Blob
  3. Use FileReader to read that file and load it into the page and/or localStorage

Let’s look at a complete example where we request an image named “rhino.png”, save it onto a blob, use FileReader to read out the file and finally save that in localStorage:


// Getting a file through XMLHttpRequest as an arraybuffer and creating a Blob
var rhinoStorage = localStorage.getItem("rhino"),
    rhino = document.getElementById("rhino");
if (rhinoStorage) {
    // Reuse existing Data URL from localStorage
    rhino.setAttribute("src", rhinoStorage);
}
else {
    // Create XHR, Blob and FileReader objects
    var xhr = new XMLHttpRequest(),
        blob,
        fileReader = new FileReader();

    xhr.open("GET", "rhino.png", true);
    // Set the responseType to arraybuffer. "blob" is an option too, rendering manual Blob creation unnecessary, but the support for "blob" is not widespread enough yet
    xhr.responseType = "arraybuffer";

    xhr.addEventListener("load", function () {
        if (xhr.status === 200) {
            // Create a blob from the response
            blob = new Blob([xhr.response], {type: "image/png"});

            // onload needed since Google Chrome doesn't support addEventListener for FileReader
            fileReader.onload = function (evt) {
                // Read out file contents as a Data URL
                var result = evt.target.result;
                // Set image src to Data URL
                rhino.setAttribute("src", result);
                // Store Data URL in localStorage
                localStorage.setItem("rhino", result);
            };
            // Load blob as Data URL
            fileReader.readAsDataURL(blob);
        }
    }, false);
    // Send XHR
    xhr.send();
}

Using “blob” as responseType

In the above example we used the responseType “arraybuffer” and a Blob to create something we could use for the FileReader. However, there’s also a responseType called “blob” which returns a blob directly that we can use with the FileReader. The above example would instead look like this then:


// Getting a file through XMLHttpRequest as an arraybuffer and creating a Blob
var rhinoStorage = localStorage.getItem("rhino"),
    rhino = document.getElementById("rhino");
if (rhinoStorage) {
    // Reuse existing Data URL from localStorage
    rhino.setAttribute("src", rhinoStorage);
}
else {
    // Create XHR and FileReader objects
    var xhr = new XMLHttpRequest(),
        fileReader = new FileReader();

    xhr.open("GET", "rhino.png", true);
    // Set the responseType to blob
    xhr.responseType = "blob";

    xhr.addEventListener("load", function () {
        if (xhr.status === 200) {
            // onload needed since Google Chrome doesn't support addEventListener for FileReader
            fileReader.onload = function (evt) {
                // Read out file contents as a Data URL
                var result = evt.target.result;
                // Set image src to Data URL
                rhino.setAttribute("src", result);
                // Store Data URL in localStorage
                localStorage.setItem("rhino", result);
            };
            // Load blob as Data URL
            fileReader.readAsDataURL(xhr.response);
        }
    }, false);
    // Send XHR
    xhr.send();
}

Web browser support

localStorage
Supported since long in all major web browsers, including IE8.
Native JSON support
Same long support as localStorage.
canvas element
Supported a long in most major web browsers, but only since IE9.
XMLHttpRequest Level 2
Supported in Firefox and Google Chrome since long, Safari 5+ and planned to be in IE10 and Opera 12.
Blob
Supported in Firefox and Google Chrome since long, and is planned to be in IE10. Also in Safari 6.0+ and Opera Opera 12.1+
FileReader
Supported in Firefox and Google Chrome since long, Opera since 11.1 and is planned to be in IE10. Unclear about Safari.
responseType “blob”
Currently only supported in Firefox. Will soon be in Google Chrome and is planned to be in IE10. Unclear about Safari and Opera.

Checking and clearing localStorage

The easiest way to check what’s in localStorage:

  • Firebug: DOM tab, then scroll down to or search for localStorage
  • Google Chrome: Developer Tools, under the Resources tab
  • Opera: In the Storage tab in Opera Dragonfly

Demo and code

I’ve put together a demo with localStorage and saving images and files in it where you can see both the canvas approach and the one using XMLHtpRequest 2, Blob and FileReader.

The code for storing files in localStorage is also available on GitHub, so go play now!

46 comments

Comments are now closed.

  1. Andrea Gimmarchi wrote on February 21st, 2012 at 03:30:

    Sorry guys, but I think this post is misleading.
    We don’t even have control over localStorage size and you guys are suggesting to store files in an object with 5Mb of limit on average per tab.
    Also you simply call LS.setItem(“key”, value) without try catch around, the only way so far to avoid “out of space” problems and keep the app/data/behavior safe and consistent or under control.
    Moreover, localStorage is synchronous and not the ideal object to use as a trash bin for big chunks of data plus the base64 (asDataURL) version of any file, since we still don’t have native gzip compression exposed through JavaScript, is on average 30% bigger than its original format.
    IndexedDB or WebSQL are way better solutions for these kind of tasks plus all these words are missing in the above article: size, limit, performances, *database*.
    Thumb down, regardless the showed code and the abstract concept behind has a good quality, unfortunately using in my opinion the worst storage option for the purpose.

    1. Chris Heilmann wrote on February 21st, 2012 at 03:44:

      Some true points in there (especially the wrapping in a try…catch) but the blnaket statement that IndexDB is always the better option is not true. For example I found quite a few apps that store in localStorage instead of indexDB as the access is much faster (albeit synchronous). So on startup of an app you can be faster when you stored images in localStorage. The base64 encoding size issue is an old one. Interestingly enough though localStorage takes UTF-16 characters, so you can double the storage with bit shifting. I am interviewing the developer of the financial times app right now and they do some very clever things indeed.

      Condemning blanket statements with other blanket statements doesn’t really help the cause – web development is always a compromise and there are great uses of localStorage without having to set up a DB and go through the issues of support across browsers.

    2. Robert Nyman wrote on February 21st, 2012 at 03:47:

      Thanks for your comment, Andrea.

      You are absolutely right about try…catch, and I’ve updated the blog post, code and demo to reflect that. With factors like size, it is always something that needs to be taken into consideration.

      WebSQL is not an option, since it’s deprecated and will never be implemented across the board. IndexedDB is definitely an interesting approach, although it’s not quite there yet with some syntactic differences across web browser implementations and lack of support so far.

      This is not meant to be the ultimate storage solution for all your files, but rather an option with simple syntax and control for some use cases. And a solution that’s more broadly available right now.

      1. Taras Glek wrote on March 2nd, 2012 at 11:59:

        What sort of logic do you want to apply? I looked at my local storage, and there are a bunch of websites there that I never visit anymore(because there is no expiry policy).

        1. Robert Nyman wrote on March 2nd, 2012 at 16:10:

          Any kind of logic that would come to mind, for me or others.

          Expiry policy will be an issue in general, though – I agree. When it comes to any kind of storage (offline, localStorage, IndexedDB) it would probably be a good idea to make inspecting/removing it easier in every web browser for end users.

    3. Taras Glek wrote on February 21st, 2012 at 15:25:

      Andrea, you are absolutely right. DOM storage < indexdb. Using it is bad for user responsiveness. So any web apps that use it cause the webpage to freeze up while accessing it.

  2. sonny wrote on February 21st, 2012 at 04:36:

    Note that WebSQL and IndexedDB allows to store Blob so you don’t need the high cpu cost operation of encoding/decoding base64 (see window.URL.createObjectURL).

    1. Robert Nyman wrote on February 21st, 2012 at 06:35:

      That Blobs are allowed for databases is great. Not sure about the mention of window.URL.createObjectURL, though?

      That works, but only in the lifetime of the current document it was created in.

      1. sonny wrote on February 21st, 2012 at 07:49:

        Yeah, I was speaking about createObjectURL so you can use the data without processing base64 data, not as a storing method.

        1. Robert Nyman wrote on February 21st, 2012 at 07:51:

          Ah, right. Yes, it works in the current document, but not for storing it in localStorage. Because if you utilize that, and then the user opens the same page in another tab, for instance, the ObjectURL won’t work.

  3. Ivan wrote on February 21st, 2012 at 05:28:

    The returned value from “typeof” is always a string. Why you type === instead of ==, why?

    1. Robert Nyman wrote on February 21st, 2012 at 06:11:

      A force of habit, really. To be it’s been good coding and to avoid any kind of type coercion to have a triple check for both type and value.

  4. Andrea Gimmarchi wrote on February 21st, 2012 at 05:34:

    Chris … on “the blnaket statement that IndexDB is always the better option is not true” indeed I have never stated that and never will.

    I have said that for files, on average a consistent quantity of data, I would *personally* never suggest localStorage because a) it’s really limited, in 5Mb you have a couple of good quality JPEG via input field if you want keep as example high res and nothing else, b) it’s synchronous, so you access in a faster (???) way synchronous data to generate something somehow asynchronous in any case as every image is, regardless the usage of data url.

    I would give localStorage its place which is not in my opinion the “file storage” but as I have said the concept behind is good in any case because is valid with all other storages too.

    WebSQL is everywhere but FF and IE, and I doubt it will disappear any soon but performances are awesome ( I remind you we are storing map tiles there with amazing results in therms of performances and on mobile ) but I have suggested IndexedDB too which has not localStorage limits plus is more suitable for pages that use more than a library and would like to store some heavier data.

    Sonny, unfortunately WebSQL does not support BLOB (while SQLite does, sigh!) or if it does, I am sure it doesn’t cross platform.

    As summary, my comment was not an attack to take personal Chris, it was rather a “warning” of things not mentioned in this article and since you guys at Mozilla care about spreading knowledge more than anyone I definitively appreciated more Robert reply and also thanks Robert for the update.

    Take care

    1. Chris Heilmann wrote on February 21st, 2012 at 05:54:

      Great stuff, I am thankful for your replies and keeping things in context. It made me talk to Robert and change the article a bit. It is a shame that we face a bit of a video moment in local DBs as well (different browser vendors supporting different formats). So I think for production I’d go for an abstraction layer that stores your files in the format supported by the browser. In the end, this is what we have to do most of the time anyways.

      I never saw this as a personal attack, I just find that a lot of people jump to the big guns of local databases quite quickly and there are legitimate uses of localStorage for files. This is not meant as a “local storage for every occasion” post. It would probably be fun to do a comparison chart and decision tree for developers. You up for it?

    2. Robert Nyman wrote on February 21st, 2012 at 06:15:

      Thanks!

      I believe it completely depends on the use case. Databases are good, but can be overkill sometimes. Also, with localStorage, sure there’s a limit, but it also allows you to store things in there without the explicit user approval. With IndexedDB and offline/appcache, there’s an extra step for the user.

      So yes, just one more option to your toolset. :-)

    3. sonny wrote on February 21st, 2012 at 07:56:

      “Sonny, unfortunately WebSQL does not support BLOB (while SQLite does, sigh!) or if it does, I am sure it doesn’t cross platform.”
      I know that Safari mobile does support it.

      1. Robert Nyman wrote on February 21st, 2012 at 08:03:

        My opinion here, on talking about options, is that the goal is to make it available for as many as possible. WebSQL will never be implemented in Internet Explorer or Firefox, whereas localStorage, IndexedDB and the various File APIs will be in all web browsers.

  5. Andrea Giammarchi wrote on February 21st, 2012 at 06:11:

    if we talk about abstraction, then yes, localStorage would be my preferred last option as fallback rather than, as example, Flash SharedObject, and for files too. Of course in this specific case articles like this are more than welcome indeed.

    IndexedDB, WebSQL, and localStorage are (or were) too different to summarize all pros and cons in a chart I guess, it would be randomly unfair for one or another choice but I’ll be more than happy to comment on that chart if you’ll ever have some time to put it together :-)

    Thanks again for the update and Best Regards

    1. Robert Nyman wrote on February 21st, 2012 at 06:16:

      Thanks for the input!

  6. Andrea Giammarchi wrote on February 21st, 2012 at 07:37:

    True, localStorage has unified behavior and no action is necessary.

    On the explicit user approval, via WebSQL, we experienced no action required in iOS if the initial size is less then or up to 5Mb, with incremental requests for 10, 25, and finally 50, or a single one when you ask directly for the limit, which is 50Mb on iOS devices.

    On Android there is no request at all, apparently, but the size is unknown in advance so 2.2 up to 10Mb, 2.3+ no problems up to 45Mb but some problem with 50Mb and never an explicit action is required.

    Finally, I can’t wait for IndexedDB to be everywhere so that we can adopt it but I hope the “asking for space” mechanism will be not that boring ( in our case it’s easy to fill up 50Mb, as example, and 5 explicit actions to take small portion of extra space are simply annoying to us ).
    To ask for space is fine, to let the developer decide for how much without limiting disk or sd access, since no native wrapper/app has such limit, would make the WebApp concept more concrete.

    Apologies this was a bit Off Topic, but hopefully useful for those that went through these comments.

    See you soon guys

    1. Robert Nyman wrote on February 21st, 2012 at 07:49:

      Yes, asking for space seems to depend. In desktop web browsers it usually doesn’t to that much of a difference, but for mobile it can matter quite a lot.

      It’s an interesting challenge, for sure!

  7. pd wrote on February 21st, 2012 at 08:56:

    ExecSum: As evil as plugins are, Flash has had local storage for how long (?) and the web still doesn’t have it to the same level of ubiquity and utility?

    Thanks for exploring this option Robert. I think it is possibly every web developer’s dream to have access to storing long-lived files client side. Not only that but everyone on the internet from users to carriers should be targeting this sort of bandwidth-saving, speed-boosting concept.

    Unfortunately the article serves mainly as a great example of where the web as a platform is still at: not very evolved. HTML5 evangelists take note! Whilst the gestation period of the localStorage feature has traversed a strange time for the web in terms of it’s evolution from a dead monopoly platform to the so-called ‘modern’ platform that exists today, localStorage is still a great case study for the evolution of web platform features because it is one of the most recent examples of the same old story: untimely, limited and ineffective APIs. However that may be a topic for another time or not (people who author this blog don’t seem to like macro-level analysis of how individual blog article topics relate to the bigger picture).

    Here’s a couple of problems I see with localStorage adoption as a augmentation or replacement of the ancient and rather arbitrary caching of files on the client-side:

    1) User permission.
    Whilst giving developers the ability to store any string on the client side naturally requires user permission due to potential privacy and security implications, why prompt users (with a message they probably don’t understand) for permission to store images and other content already stored in cache without the need to obtain permission? I foresee limited acceptance from end users who might get tired of being prompted and therefore limited acceptance from developers who rarely have the time or energy to implement solutions that are not guaranteed to be widely adopted.

    On a related but small note, Firefox’s local storage prompt doesn’t even seem to have a help section associated with it whereas Flash has this:

    http://www.macromedia.com/support/documentation/en/flashplayer/help/help06.html

    2) Overhead. There seems to be a substantial amount of work required. Don’t label me as lazy. I just wonder how much *more* work would be required to organise fallback cases; maintain the the storage space over time (updating content when required, ensure the 5MB limit is not exceeded); measure the impact on performance over time (stats) and so on. It really needs a library like jQuery to adopt this and simplify it.

    Nevertheless thanks again for looking into this. Aside from the weird “since long” phrase, this is a well-written reference of the current state of the web’s evolution beyond clunky caching. Unfortunately that current state of evolution still amounts to, IMHO, not a very workable solution just yet but there’s some hope at least. In the meantime … I work in Australia where Microsoft is supposed to have force-upgraded everyone to at least IE8. Yet stats still state 11% of visitors to my employer’s primary website still run IE7. Combine that limitation with those outlined in your “Web browser support” section and there’s a great example of the failures of the web platform to provide timely meaningful APIs immediately prior to, and I guess during, the ‘modern’ HTML5 era. I hope those implementing HTML5 can learn from this recent example.

    1. Robert Nyman wrote on February 21st, 2012 at 09:14:

      Thanks, glad you liked the article!

      I think, for me, it can be summed up pretty easily: The Flash runtime is a closed technology working on some platforms, whereas HTML5 is developed in the open and runs on “every” platform and web browser, without needing any plugin.

      To me, that’s an enormous upside.

      > people who author this blog don’t seem to like macro-level analysis of how individual blog article topics relate to the bigger picture

      I’m actually a bit saddened to read this. You have a constructive comment that discusses important things. Please don’t waste it on remarks like that.

      When it comes to your points:

      1. There is no prompt in web browsers for localStorage, as opposed to IndexedDB and offline/appcache (your mileage may vary here, depending on implementation).

      2. Overhead is of course a relative term, and it’s up to everyone to use it if/when they see fit. If it just plain caching, perhaps you’re better off with expires headers and regular web browser cache. But if you want to tailor and customize something with logic after a certain need, this could prove to be an option.

      I would also argue that it is not HTML5’s fault that IE7 is still around, and that it took until IE9 before canvas was implemented in Internet Explorer. The IE team is nowadays offering a good amount of improvements, and various standards become more and more implemented across the board.

  8. pd wrote on February 22nd, 2012 at 08:37:

    > people who author this blog don’t seem to like macro-level analysis of how individual blog article topics relate to the bigger picture

    >> I’m actually a bit saddened to read this. You have a constructive comment that discusses important things. Please don’t waste it on remarks like that.

    Sorry to make you sad but it saddens me further that you are unable to tolerate even one relatively balanced, mildly critical sentence in amongst the rest of my points. When did the web become a medium of censorship? At the heart of that sentence is the impression that there needs to be something better than this website currently provides, for discussing topics that may relate to one single blog article but also touch on bigger, broader issues. The authors of this blog chose the blog format and all that entails including limiting comments to the single blog post topic where they appear. Either the authors of this blog or another part of Mozilla could just as easily choose to use a forum format to present this content and that format would hopefully be more open to topics of broader discussion. That’s the main thing I was trying to point out. Boy do I feel guilty writing this much content in a blog post. Why should I? If there was a developer forum for web developers run by Mozilla, I wouldn’t have to. Yes there’s the Google Groups but who wants to run the risk of being flamed in there? A forum might well be more welcoming I guess is what I’m trying to say.

    Re Flash, IMHO I think you’re missing the point I’m trying to make: it’s not political so much as practical. I realize that Mozilla tends to place the political in front of the practical often and I can see the merit of that approach in that it helps to push the open web agenda. However there’s more than one way to push the open web agenda. Creating open alternatives to closed features much faster will hopefully prevent us ever seeing a dominant closed-source plugin like Flash again. The point is speed. Developers need features right now, yesterday in fact, they always will. Not all developers can adopt the not-necessarily-practical stance of ignoring features available through a closed-source runtime simply because it’s closed. ‘Selling’ that politics to clients and managers is very hard. Whereas if developers have a clear choice between open and closed, with little or no difference in either solution, I expect most would select the open (browser not plugin) option every time. This is where the recent upsurge in new APIs in browser land has been great. Sure it took two years to make Firefox usable again after the flood of new APIs pushed into Firefox 4 but if that could have been avoided it is definitely the better option to push out new features ASAP if the goal is to cut off the threat of closed-source options at the pass. This is why I like HTML5. I also hate it’s lack of rudimentary form widgets but at least I like it somewhat as well :)

    Re point one, I guess there are that many storage options battling for mindshare so it was easy to mistake the prompt for indexedDB permission as one for localStorage. Regardless I think my point about the need for help content accessible from this prompt is still relevant. There’s a doorhanger and a slide-down prompt and neither of them have any link to help content that explains to users what they are being asked. There’s a big blue circular question mark icon on the doorhanger but nothing else.

    Re point two, if not for the huge lag between HTML4 and HTML5, highlighted by the need to form WHATWG and set aside XHTML, would IE7 have had more next-generation standards-based API features and therefore IE8 also? I reckon they might have and therefore it would matter less that IE7 is still around. Even Microsoft, despite developing SilverLight, seem to support a fairly reasonable subset of open standards when those standards are actually agreed upon and ready to be implemented. Criticism of Microsoft for innovating with proprietary or future-gazing, not-yet-standadized features, was massive. I laughingly sit back now and watch Mozilla, Google and Apple all do the same thing, perhaps to different degrees.

    I’m fully aware that MS stopped developing their browser and that they had a hard time overcoming compatibility problems with applications written to their monopoly-generated proprietary standards. No doubt this held IE7 back in terms of it’s standards support. On the other hand, IE7 was developed in 2005 if it’s public preview release date of Jan 31 2006 is anything to go by. HTML5 wasn’t even adopted by the W3C HTML working group until 2007. That is, unless wikipedia is lying :) Web Applications 1 and Web Forms 2 might have been gestating at the same time IE7 was being developed but is it fair to expect MS to implement any modern API in IE7 given the state of standardization politics at the time? Forms on the web still haven’t improved much to this very day and the infamous completion date for HTML5 is still a decade away!

    It may not be HTML5’s fault that IE7 still exists, but it’s not necessarily IE7’s fault that it’s such a red herring due to it’s limited HTML5 support either.

    If open-web advocates don’t provide the hymn book, they can’t expect everyone to sing.

    1. Robert Nyman wrote on February 22nd, 2012 at 11:30:

      pd,

      Mozilla definitely represents the political part of the web, but what I was talking about couldn’t be more practical – reaching as many operating systems, web browsers and devices as possible.

      In terms of support: the canvas element has been around in Firefox since version two, Safari since 3.1, all versions of Google Chrome and Opera since version 9 – meaning many many years. Not in Internet Explorer till version 9.

      Almost the same situation with localStorage, although one version earlier in Internet Explorer. So yes, a lot of this could have been in Internet Explorer 7. It was in WHATWG and it was in all other web browsers.

      But why worry about the past? It’s in there, and support is getting better in all web browsers all the time.

  9. Joan wrote on February 25th, 2012 at 11:31:

    Wow, I’ve just read this post and I’ve done something really similar (unrelated) at https://github.com/jcreus/localcache . It allows to store CSS, JavaScript and images, and acts as a cache.

    1. Robert Nyman wrote on February 26th, 2012 at 07:32:

      Cool, thanks for sharing!
      Next step would be to utilize IndexedDB if it’s available – as in http://hacks.mozilla.org/2012/02/storing-images-and-files-in-indexeddb/.

      1. Joan Creus wrote on February 26th, 2012 at 07:49:

        Ok! I’ll look into it, looks cool (I hadn’t heard of it before). Does it give advantages? Maybe allowing 10 MB? (5 in local and 5 in the DB). Or speed improvements? Thanks!

        1. Robert Nyman wrote on February 26th, 2012 at 14:26:

          With IndexedDB, storage limits depend on the web browser, but in Firefox you have 50MBs of storage. And if you exceed that, the user will be asked for approval, and you can have more.

          IndexedDB is much better suited for more structured data, and given the different natures of the APIs with IndexedDB being async, you should get better performance there.

          We will have a more detailed blog post on localStorage on this blog later.

  10. Mihai Chereji wrote on February 29th, 2012 at 16:55:

    Hello, thanks for the write-up, I’ll be using it briefly in a WordPress plug-in I’m developing.

    My chief concern now is, is there any way to actually download a certain file created by a web-app on the client harddrive (without the need for server interaction)? Basically, what I need right now is something like this library is trying to achieve, but with a few extra features. I’ve been racking my brains for a few days, and I thought I might give it a shot, writing here, as this seems to be kind of related.

    Thanks again.

    1. Robert Nyman wrote on March 1st, 2012 at 04:31:

      Yes, if you have anything as a Data URL/base64-encoded string, you need to use such an approach in the linked library to get to save the actual file.

  11. Cezary Tomczyk wrote on March 2nd, 2012 at 02:23:

    Personally, I do not see the application here localStorage for images. Just send pictures from a server with the header “Expires” and the browser will have them in it’s local cache.

    1. Robert Nyman wrote on March 2nd, 2012 at 02:53:

      The difference with Expires headers is that you can’t apply any logic to it. Once they’re in the web browser cache, they have to expire or be flushed out of there. With this, you can choose whatever checks you want to decide which image to get, different versions of the same image etc.

      1. Cezary Tomczyk wrote on March 2nd, 2012 at 03:18:

        Yes, it’s true. But, until there is no need to make any logic to images, just store in local browser cache is enought.

        1. Robert Nyman wrote on March 2nd, 2012 at 03:19:

          Definitely. It’s all about your own use case.

  12. mihai wrote on March 2nd, 2012 at 02:40:

    I use localStorage for saving (important?) stuff when the user interacts with the browser but the connection is down (then, sync it later, if possible). So in that way, it’s important.

    1. Robert Nyman wrote on March 2nd, 2012 at 02:54:

      Yes, syncing is another aspect of it.

  13. Mohamed Jama wrote on June 27th, 2012 at 04:15:

    Great article, Thanks robert.
    I am just wondering if there is a way to check if the browser is on private browsing mode and if it is then see what options you have.

    1. Robert Nyman wrote on June 28th, 2012 at 02:23:

      Thank you!
      I hope the information in Supporting private browsing mode will help you there.

  14. Rani wrote on October 4th, 2012 at 06:35:

    I’m reading the comments here and it seems that I have the same issue as some of you here. I got the answers from Robert’s responses so I lost the fuss now. Anyways my concern is about the suitability of IndexedDB for structured data. Thanks for clearing my brain today!

    1. Robert Nyman wrote on October 4th, 2012 at 06:39:

      Glad that you seem to have find your answers.

  15. Sam wrote on November 17th, 2012 at 21:22:

    I have been looking for ways to store web app images into a new library. It seems that localStorage is a good option and converting images into a Data URL to be accepted in location is not difficult to do so I guess I will go for this. Thanks for the info!

  16. ben wrote on December 6th, 2012 at 08:49:

    Hi, the last option is awesome! is there anyway to make it crossbrowser?

    1. Robert Nyman wrote on December 6th, 2012 at 08:58:

      Some functionality is being dependent on web browser support, unfortunately.

  17. Sid wrote on December 19th, 2012 at 04:20:

    I am able to see the image on IE8. Does that mean IndexedDB is working for me? Moreover I am not able to run any IDB demo on Firefox 17, or alpha or nightly. The same works on Chrome. I tried creating a new profile but same luck. :( Am I doing something wrong.

    I am the same guy who commented on one of your blog posts too ;)

    1. Robert Nyman wrote on December 19th, 2012 at 16:09:

      You are probably referring to Storing images and files in IndexedDB. And if you do that, it definitely shouldn’t work in IE8. What it does, though, is showing a normal image if the operation doesn’t work.

      Otherwise it gets that same image from IndexedDB and then replaces the current one on the page. So, use the developer tools in your web browser to check the src attribute of the image.

      I just tested the IndexedDB demo in Firefox Aurora and it worked fine. If the problem persists, please file a bug.

Comments are closed for this article.