原文地址:http://hacks.mozilla.org/2012/02/saving-images-and-files-in-localstorage/
你可能已经对本地存储有所了解,本地存储在浏览器中快速存储数据的时候特别强大,并且已经在浏览器中存在多时。但是如何才能在本地存储中保存文件呢?
首先,推荐你先阅读Storing images and files in IndexedDB。
首先,我们先了解一些基本的本地存储相关的知识。你可以使用键值对的方式往本地存储中存储数据,就像这样:
localStorage.setItem("name", "Robert");
而从本地存储中读取数据的方式如下:
localStorage.getItem("name");
这样的存取方式非常不错,而且最多可以存储5M的数据,给你更多选择的空间。但是由于本地存储是基于字符串的存储,存储一串没有结构的字符串并不是一个理想的选择。因此,我们可以利用浏览器中原生的JSON支持来将JavaScript对象转化成字符串,从而保存到本地数据中,在读取的时候也可以将其转换回JavaScript对象。
我们的想法是做到将已经当前页面中已缓存的图片保存到本地存储中。不过就像我们之前已经确定的,本地存储只支持字符串的存取,那么我们要做的就是将图片转换成Data URI。其中一种实现方式就是用canvas元素来加载图片。然后你可以以Data URI的形式从canvas中读取出当前展示的内容。
让我们看一个例子。
//当图片加载完成的时候触发回调函数 elephant.addEventListener("load", function () { var imgCanvas = document.createElement("canvas"), imgContext = imgCanvas.getContext("2d");
// 确保canvas元素的大小和图片尺寸一致 imgCanvas.width = elephant.width; imgCanvas.height = elephant.height;
// 渲染图片到canvas中 imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height);
// 用data url的形式取出 var imgAsDataURL = imgCanvas.toDataURL("image/png");
// 保存到本地存储中 try { localStorage.setItem("elephant", imgAsDataURL); } catch (e) { console.log("Storage failed: " + e); } }, false);
如果我们想要考虑地更长远一些,那么还可以利用JavaScript对象并做一些数据检查。在这个例子中,第一次我们从服务端读取图片,之后每一次页面加载时,我们就可以直接从本地存储中读取已读取过的图片。
id="elephant" src="about:blank" alt="A close up of an elephant"> src="elephant.png" alt="A close up of an elephant">A mighty big elephant, and mighty close too!
//在本地存储中保存图片 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(); // 检查数据,如果不存在或者数据过期,则创建一个本地存储 if (typeof storageFilesDate === "undefined" || storageFilesDate < todaysDate) { // 图片加载完成后执行 elephant.addEventListener("load", function () { var imgCanvas = document.createElement("canvas"), imgContext = imgCanvas.getContext("2d"); // 确保canvas尺寸和图片一致 imgCanvas.width = elephant.width; imgCanvas.height = elephant.height; // 在canvas中绘制图片 imgContext.drawImage(elephant, 0, 0, elephant.width, elephant.height); // 将图片保存为Data URI storageFiles.elephant = imgCanvas.toDataURL("image/png");
storageFiles.date = todaysDate; // 将JSON保存到本地存储中 try { localStorage.setItem("storageFiles", JSON.stringify(storageFiles)); } catch (e) { console.log("Storage failed: " + e); } }, false); // 设置图片 elephant.setAttribute("src", "elephant.png"); } else { // Use image from localStorage elephant.setAttribute("src", storageFiles.elephant); }
注意:此处需要注意本地存储的容量,最好使用try...catch来控制异常。
使用canvas将图片转换成Data URI并保存到本地存储中的方式非常好,但是如果我们希望能找到一个可以保存任意格式文件的方式。
那么,这个过程就显的比较有趣了,我们需要用到:
基本方法是:
// 获取文件 var rhinoStorage = localStorage.getItem("rhino"), rhino = document.getElementById("rhino"); if (rhinoStorage) { //如果已经存在则直接重用已保存的数据 rhino.setAttribute("src", rhinoStorage); } else { // 创建XHR, BlobBuilder 和FileReader 对象 var xhr = new XMLHttpRequest(), blobBuilder = new (window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.OBlobBuilder || window.msBlobBuilder), blob, fileReader = new FileReader(); xhr.open("GET", "rhino.png", true); //将响应头类型设置为“arraybuffer”,也可以使用"blob",这样就不需要使用BlobBuilder来构建数据,但是"blob"的支持程度有限。 xhr.responseType = "arraybuffer"; xhr.addEventListener("load", function () { if (xhr.status === 200) { // 将响应数据放入blobBuilder中 blobBuilder.append(xhr.response); // 用文件类型创建blob对象 blob = blobBuilder.getBlob("image/png"); // 由于Chrome不支持用addEventListener监听FileReader对象的事件,所以需要用onload fileReader.onload = function (evt) { // 用Data URI的格式读取文件内容 var result = evt.target.result; // 将图片的src指向Data URI rhino.setAttribute("src", result); //保存到本地存储中 try { localStorage.setItem("rhino", result); } catch (e) { console.log("Storage failed: " + e); } }; // 以Data URI的形式加载blob fileReader.readAsDataURL(blob); } }, false); // 发送异步请求 xhr.send(); }
在上面的例子中,我们使用的是“arraybuffer”作为响应头类型,然后使用BlobBuilder来创建可以由FileReader读取的数据。然而,"blob"作为响应头类型后,会直接返回一个blob对象,从而可以直接由FileReader读取。上面的例子可以改成这样:
// 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, BlobBuilder and FileReader objects var xhr = new XMLHttpRequest(), fileReader = new FileReader(); xhr.open("GET", "rhino.png", true); // Set the responseType to arraybuffer. "blob" is an option too, rendering BlobBuilder unnecessary, but the support for "blob" is not widespread enough yet 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 try { localStorage.setItem("rhino", result); } catch (e) { console.log("Storage failed: " + e); } }; // Load blob as Data URL fileReader.readAsDataURL(xhr.response); } }, false); // Send XHR xhr.send(); }