Saving files locally using Web Storage
This topic starts where Reading local files left off, and demonstrates how to save non-large files locally using Web Storage.
- Canvas drawing application
- Saving files using Web Storage
- Exercises
- Related topics
One of the easiest ways to save moderately sized files (locally) is through the use of Web Storage. Web Storage is similar to traditional cookies but provides typically larger storage allocation. For example, Internet Explorer 10 allows 10 MG per domain to locally store various items such as strings, files, JavaScript objects, and so on. The following code examples demonstrate how to save files locally using this technique.
Note The following code examples require a browser that supports File API and Web Storage, such as Internet Explorer 10 or later.
Canvas drawing application
In order to make the subsequent code examples more realistic, the first example introduces a simple HTML5 canvas based drawing application. Such applications provide a legitimate need to save files locally. This example will be subsequently extended in order to save one’s artist achievements locally.
<!DOCTYPE html>
<html>
<head>
<title>Simple Drawing App</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<style>
html {
-ms-touch-action: none;
text-align: center; /* Center all contents of the page. */
}
</style>
</head>
<body>
<h1>Simple Drawing App</h1>
<h3>Example 1</h3>
<canvas id="drawSurface" width="500" height="500" style="border:1px solid black;"></canvas> <!-- The canvas element can only be manipulated via JavaScript -->
<div>
<button id="erase">Erase</button>
</div>
<script>
if (!document.createElement('canvas').getContext) {
document.querySelector('body').innerHTML = "<h1>Canvas is not supported by this browser.</h1><p>To use this application, upgrade your browser to the latest version.</p>";
}
else {
window.addEventListener('load', init, false); // Safety first.
function init() {
var canvas = document.getElementById('drawSurface'); // A static variable, due to the fact that one or more local functions access it.
var context = canvas.getContext('2d'); // A static variable, due to the fact that one or more local functions access it.
context.fillStyle = "purple";
if (window.navigator.msPointerEnabled) {
canvas.addEventListener('MSPointerMove', paintCanvas, false);
}
else {
canvas.addEventListener('mousemove', paintCanvas, false);
}
document.getElementById('erase').addEventListener('click', eraseCanvas, false);
function paintCanvas(event) { // The "event" object contains the position of the pointer/mouse.
context.fillRect(event.offsetX, event.offsetY, 4, 4); // Draw a 4x4 rectangle at the given coordinates (relative to the canvas box). As of this writing, not all browsers support offsetX and offsetY.
} // paintCanvas
function eraseCanvas() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
} // eraseCanvas
} // init
} // else
</script>
</body>
</html>
To use this simplistic application, move your mouse over the square canvas area. To erase a drawing, simply click the Erase button.
Perhaps the most complex aspect of this code are the two "static" variables, canvas
and context
, declared as locals within init
. Because local functions (paintCanvas
and eraseCanvas
) access them, they are accessible after init
has terminated. This has the benefit of not cluttering up the global namespace. Note that this application also works with touch-enable devices, such as a tablet.
Saving files using Web Storage
As mentioned above, modern browsers (including Windows Internet Explorer 9 and later) support Web Storage. There are two types of Web Storage, local storage and session storage. As the name suggests, session storage only persists for the given browser session. Local storage, on the other hand, persists indefinitely.
We will use local storage to save a user’s drawing. This turns out to be relatively trivial, as the following example shows:
<!DOCTYPE html>
<html>
<head>
<title>Simple Drawing App</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
<style>
html {
-ms-touch-action: none;
text-align: center; /* Center all contents of the page. */
}
</style>
</head>
<body id="bodyElement"> <!-- This ID is used in the following script block for feature detection. -->
<h1>Simple Drawing App</h1>
<h3>Example 2</h3>
<canvas id="drawSurface" width="500" height="500" style="border:1px solid black;"></canvas> <!-- The canvas element can only be manipulated via JavaScript -->
<div>
<button id="erase">Erase</button>
<button id="save">Save</button>
<button id="load">Load</button>
</div>
<script>
function requiredFeaturesAvailable() {
return (
!!window.addEventListener && // Use the double negative "!!" to force the object to a Boolean value.
!!document.createElement('canvas').getContext &&
!!window.localStorage
);
} // requiredFeaturesAvailable
if ( !requiredFeaturesAvailable() ) {
document.getElementById('bodyElement').innerHTML = "<h2>Required features are not supported by this browser.</h2><p>To use this application, upgrade your browser to the latest version.</p>";
}
else {
window.addEventListener('load', init, false); // Safety first.
function init() {
var canvas = document.getElementById('drawSurface'); // A static variable, due to the fact that one or more local functions access it.
var context = canvas.getContext('2d'); // A static variable, due to the fact that one or more local functions access it.
context.fillStyle = "purple";
if (window.navigator.msPointerEnabled) {
canvas.addEventListener('MSPointerMove', paintCanvas, false);
}
else {
canvas.addEventListener('mousemove', paintCanvas, false);
}
document.getElementById('erase').addEventListener('click', eraseCanvas, false);
document.getElementById('save').addEventListener('click', saveCanvas, false);
document.getElementById('load').addEventListener('click', loadCanvas, false);
function paintCanvas(event) { // The "event" object contains the position of the pointer/mouse.
context.fillRect(event.offsetX, event.offsetY, 4, 4); // Draw a 4x4 rectangle at the given coordinates (relative to the canvas box). As of this writing, not all browsers support offsetX and offsetY.
} // paintCanvas
function saveCanvas() {
window.localStorage.canvasImage = canvas.toDataURL(); // Save the user's drawing to persistent local storage.
} // saveCanvas
function eraseCanvas() {
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
} // eraseCanvas
function loadCanvas() {
var img = new Image(); // The canvas drawImage() method expects an image object.
img.src = window.localStorage.canvasImage; // Retrieve the last saved artistic achievement from persistent local storage.
img.onload = function() { // Only render the saved drawing when the image object has fully loaded the drawing into memory.
context.drawImage(img, 0, 0); // Draw the image starting at canvas coordinate (0, 0) - the upper left-hand corner of the canvas.
} // onload
} // loadCanvas
} // init
} // else
</script>
</body>
</html>
Example 2 is essentially example 1 with the following extensions:
We first detect for all required features we are using with the
requiredFeaturesAvailable
function. As a safety precaution, we double-negate the objects of interest to "cast" the object to a Boolean value.Next we add two new event handlers,
saveCanvas
anderaseCanvas
, that are respectively invoked when the Save and Load buttons are clicked. ThesaveCanvas
function consists of one line:window.localStorage.canvasImage = canvas.toDataURL();
This converts the user’s drawing into a form that can be displayed on a webpage and then saves it to local storage under the custom
canvasImage
property name (this can be any valid name you like).When the Load button is clicked, we retrieve the saved image and render it on the canvas as follows: Because the
canvas.drawImage
method expects an image, we first create a new generic image object, set itssrc
attribute to the saved drawing (this is usually set to an HTTP path pointing to a graphics file), and then when the image objects tells us that it’ssrc
property is fully loaded, we transfer it to the canvas element by invokingcanvas.drawImage
.
To convince yourself that the drawing really is persistently saved, create a drawing, click Save, restart your computer, start example 2 again, and then (being careful not to accidentally draw on the blank canvas), click Load – the saved drawing "miraculously" appears.
As an aside, be aware that Web Storage only allows you to store string-based key/value pairs. Despite this, it is possible to store raw JavaScript objects using JSON.stringify(). The following example demonstrates how this can be done:
<!DOCTYPE html>
<html>
<head>
<title>Storing/Retrieving JavaScript Objects Using HTML5 Local Storage</title>
<meta http-equiv="X-UA-Compatible" content="IE=10">
</head>
<body>
<h1>Storing Objects using Local Storage</h1>
<h3>Example 3</h3>
<p></p>
<script>
if (!!window.localStorage) {
var person = { firstName: 'John', lastName: 'Anderson' }; // Create a JavaScript object literal.
window.localStorage.person = JSON.stringify(person); // Convert the object to a string.
person = JSON.parse(window.localStorage.person); // Convert the object string back to a JavaScript object.
document.querySelector('p').innerHTML = "<strong>First Name:</strong> " + person.firstName + "<br /><strong>Last Name:</strong> " + person.lastName;
}
else {
document.querySelector('body').innerHTML = "<h2>Local storage is not supported by this browser.</h2><p>To use this application, upgrade your browser to the latest version.</p>";
}
</script>
</body>
</html>
Exercises
Clearly, the above drawing application is rudimentary at best. It is suggested that, at a minimum, you improve this application such that the user must first click on the canvas before drawing starts, and that the "blocky" rectangles (sensitive to mouse/pointer speed) be replaced with contiguously flowing lines.