w3ctech

【译】在本地存储中保存图片和文件

原文地址:http://hacks.mozilla.org/2012/02/saving-images-and-files-in-localstorage/

本文由小豪翻译

你可能已经对本地存储有所了解,本地存储在浏览器中快速存储数据的时候特别强大,并且已经在浏览器中存在多时。但是如何才能在本地存储中保存文件呢?

首先,推荐你先阅读Storing images and files in IndexedDB

使用JSON实现强大的本地存储控制

首先,我们先了解一些基本的本地存储相关的知识。你可以使用键值对的方式往本地存储中存储数据,就像这样:

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对象并做一些数据检查。在这个例子中,第一次我们从服务端读取图片,之后每一次页面加载时,我们就可以直接从本地存储中读取已读取过的图片。

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部分

//在本地存储中保存图片
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并保存到本地存储中的方式非常好,但是如果我们希望能找到一个可以保存任意格式文件的方式。

那么,这个过程就显的比较有趣了,我们需要用到:

  • XMLHttpRequest Level 2
  • BlobBuilder:提供接口来构建Blob对象,Blob对象是BLOB (binary large object),二进制大对象,是一个可以存储二进制文件的容器。在计算机中,BLOB常常是数据库中用来存储二进制文件的字段类型。BLOB是一个大文件,典型的BLOB是一张图片或一个声音文件,由于它们的尺寸,必须使用特殊的方式来处理(例如:上传、下载或者存放到一个数据库)。
  • FileReader

基本方法是:

  1. 用XMLHttpRequest请求文件,然后将响应头设置为"arraybuffer"。
  2. 将返回数据存放到BlobBuilder中
  3. 获取blob,也就是文件内容
  4. 使用FileReader对象读取文件并加载到文件中,最后保存到本地存储。
// 获取文件
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();
}

使用“blob”作为响应头类型

在上面的例子中,我们使用的是“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();
}

浏览器支持情况

  • 本地存储——大部分主流浏览器都支持(在国外=。=), including IE8.
  • 原生的JSON支持——支持情况和本地存储类似
  • canvas元素——大部分主流浏览器都支持,从IE9开始
  • XMLHttpRequest Level 2—— Firefox,Google Chrome, Safari 5+ 并计划在IE10和Opera 12中实现
  • BlobBuilder——Firefox ,Google Chrome,并计划在IE10中实现. Safari和Opera情况不明.
  • FileReader——Firefox ,Google Chrome,Opera 11.1之后的版本, 计划在IE10中实现. Safari情况不明.
  • responseType “blob”——只在Firefox中支持. Google Chrome即将支持,IE10计划支持. Safari和Opera情况不明。
w3ctech微信

扫码关注w3ctech微信公众号

共收到0条回复