Storing images and files in IndexedDB
This post was originally published for Mozilla Hacks.
The desired future approach for storing things client-side in web browsers is utilizing IndexedDB. Here I’ll walk you through how to store images and files in IndexedDB and then present them through an ObjectURL.
The general approach
First, let’s talk about the steps we will go through to create an IndexedDB data base, save the file into it and then read it out and present in the page:
- Create or open a database.
- Create an objectStore (if it doesn’t already exist)
- Retrieve an image file as a blob
- Initiate a database transaction
- Save that blob into the database
- Read out that saved file and create an ObjectURL from it and set it as the src of an image element in the page
Creating the code
Let’s break down all parts of the code that we need to do this:
Create or open a database.
// IndexedDB var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB, IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction, dbVersion = 1.0; // Create/open database var request = indexedDB.open("elephantFiles", dbVersion);
request.onsuccess = function (event) {
console.log(“Success creating/accessing IndexedDB database”);
db = request.result;
db.onerror = function (event) {
console.log(“Error creating/accessing IndexedDB database”);
};
// Interim solution for Google Chrome to create an objectStore. Will be deprecated
if (db.setVersion) {
if (db.version != dbVersion) {
var setVersion = db.setVersion(dbVersion);
setVersion.onsuccess = function () {
createObjectStore(db);
getImageFile();
};
}
else {
getImageFile();
}
}
else {
getImageFile();
}
}
// For future use. Currently only in latest Firefox versions
request.onupgradeneeded = function (event) {
createObjectStore(event.target.result);
};
The intended way to use this is to have the onupgradeneeded
event triggered when a database is created or gets a higher version number. This is currently only supported in Firefox, but will soon be in other web browsers. If the web browser doesn’t support this event, you can use the deprecated setVersion
method and connect to its onsuccess
event.
Create an objectStore (if it doesn’t already exist)
// Create an objectStore console.log("Creating objectStore") dataBase.createObjectStore("elephants");
Here you create an ObjectStore that you will store your data – or in our case, files – and once created you don’t need to recreate it, just update its contents.
Retrieve an image file as a blob
// Create XHR and BlobBuilder var xhr = new XMLHttpRequest(), blob; xhr.open("GET", "elephant.png", true); // Set the responseType to blob xhr.responseType = "blob"; xhr.addEventListener("load", function () { if (xhr.status === 200) { console.log("Image retrieved"); // File as response blob = xhr.response; // Put the received blob into IndexedDB putElephantInDb(blob); } }, false); // Send XHR xhr.send();
This code gets the contents of a file as a blob
directly. Currently that’s only supported in Firefox.
Once you have received the entire file, you send the blob to the function to store it in the database.
Initiate a database transaction
// Open a transaction to the database var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);
To start writing something to the database, you need to initiate a transaction with an objectStore name and the type of action you want to do – in this case read and write.
Save that blob into the database
// Put the blob into the dabase transaction.objectStore("elephants").put(blob, "image");
Once the transaction is in place, you get a reference to the desired objectStore and then put your blob into it and give it a key.
Read out that saved file and create an ObjectURL from it and set it as the src of an image element in the page
// Retrieve the file that was just stored transaction.objectStore("elephants").get("image").onsuccess = function (event) { var imgFile = event.target.result; console.log("Got elephant!" + imgFile); // Get window.URL object var URL = window.URL || window.webkitURL; // Create and revoke ObjectURL var imgURL = URL.createObjectURL(imgFile); // Set img src to ObjectURL var imgElephant = document.getElementById("elephant"); imgElephant.setAttribute("src", imgURL); // Revoking ObjectURL URL.revokeObjectURL(imgURL); };
Use the same transaction to get the image file you just stored, and then create an objectURL and set it to the src
of an image in the page.
This could just as well, for instance, have been a JavaScript file that you attached to a script
element, and then it would parse the JavaScript.
The complete code
So, here’s is the complete working code:
(function () { // IndexedDB var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB, IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction, dbVersion = 1.0; // Create/open database var request = indexedDB.open("elephantFiles", dbVersion), db, createObjectStore = function (dataBase) { // Create an objectStore console.log("Creating objectStore") dataBase.createObjectStore("elephants"); }, getImageFile = function () { // Create XHR and BlobBuilder var xhr = new XMLHttpRequest(), blob; xhr.open("GET", "elephant.png", true); // Set the responseType to blob xhr.responseType = "blob"; xhr.addEventListener("load", function () { if (xhr.status === 200) { console.log("Image retrieved"); // Blob as response blob = xhr.response; // Put the received blob into IndexedDB putElephantInDb(blob); } }, false); // Send XHR xhr.send(); }, putElephantInDb = function (blob) { console.log("Putting elephants in IndexedDB"); // Open a transaction to the database var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE); // Put the blob into the dabase transaction.objectStore("elephants").put(blob, "image"); // Retrieve the file that was just stored transaction.objectStore("elephants").get("image").onsuccess = function (event) { var imgFile = event.target.result; console.log("Got elephant!" + imgFile); // Get window.URL object var URL = window.URL || window.webkitURL; // Create and revoke ObjectURL var imgURL = URL.createObjectURL(imgFile); // Set img src to ObjectURL var imgElephant = document.getElementById("elephant"); imgElephant.setAttribute("src", imgURL); // Revoking ObjectURL URL.revokeObjectURL(imgURL); }; }; request.onerror = function (event) { console.log("Error creating/accessing IndexedDB database"); }; request.onsuccess = function (event) { console.log("Success creating/accessing IndexedDB database"); db = request.result; db.onerror = function (event) { console.log("Error creating/accessing IndexedDB database"); }; // Interim solution for Google Chrome to create an objectStore. Will be deprecated if (db.setVersion) { if (db.version != dbVersion) { var setVersion = db.setVersion(dbVersion); setVersion.onsuccess = function () { createObjectStore(db); getImageFile(); }; } else { getImageFile(); } } else { getImageFile(); } } // For future use. Currently only in latest Firefox versions request.onupgradeneeded = function (event) { createObjectStore(event.target.result); }; })();
Web browser support
- IndexedDB
- Supported since long (a number of versions back) in Firefox and Google Chrome. Planned to be in IE10, unclear about Safari and Opera.
- onupgradeneeded
- Supported in latest Firefox. Planned to be in Google Chrome soon and hopefully IE10. Unclear about Safari and Opera.
- Storing files in IndexedDB
- Supported in Firefox 11 and later. Planned to be supported in Google Chrome. Hopefully IE10 will support it. Unclear about Safari and Opera.
- XMLHttpRequest Level 2
- Supported in Firefox and Google Chrome since long, Safari 5+ and planned to be in IE10 and Opera 12.
- 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.
Demo and code
I’ve put together a demo with IndexedDB and saving images and files in it where you can see it all in action. Make sure to use any Developer Tool to Inspect Element on the image to see the value of its src
attribute. Also make sure to check the console.log messages to follow the actions.
The code for storing files in IndexedDB is also available on GitHub, so go play now!
I don’t think I’ve seen any committed plans on Chrome supporting blobs in IDB. (I’d love to see this btw, but right now it overlaps with the functionality Chrome provides in FileSystem API. Shrug.). If you got a ticket, link it up?
http://hacks.mozilla.org/2012/03/there-is-no-simple-solution-for-local-storage/
Even Mr Heilmann doesn’t seem to believe in this ‘solution’ ATM.
Paul,
Here’s the ticket for IndexedDB should support storing File/Blob objects.
pd,
Thanks for informing me what’s in the blog I work on every day. 🙂
IndexedDB is the viable option for storing things in the long run, especially files.
[…] indexeddb? ???? ?? ???? […]
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.
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.
Thanks a lot. IDB demos occasionally works on Firefox. I will file a bug. 🙂
Hi,
File storage is not working in Chrome. urgent requirement for me is to store and retreive a pdf in indexeddb especially in Google Chrome.
🙁
Rajesh,
The code in this post has been tested to work with Chrome. If it doesn’t work for you, feel free to file a bug with Chrome, or maybe evaluate other options.
Unfortunately this does not work in chrome. Also no support for iOS devices.
Is there a way to have a cross-platform solution to storing BLOBs in a mobile device?
I think it will be in Chrome soon, but not sure about Safari and iOS.Another option could be to use localStorage.