在本地存储localStorage/sessionStorage中保存图片和文件(真的不适合保存文件图片大数据)

本文介绍如何利用本地存储技术保存图片和文件,包括使用Data URI存储图片、通过XMLHttpRequest和BlobBuilder保存任意格式文件的方法。

localStorage支持还算广泛,ie8都有支持,存储量多数≈5M,也还可以,但是要是存储图片和文件,感觉就有些捉襟见肘,不过如有需要还是了解一下这方面的技术比较好。 
译文来自:http://w3ctech.com/p/1061,下面是译文的正文内容,关于如何存储图片和文件到本地存储。 

原文地址: 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情况不明。
<think>好的,我现在需要帮助用户找到在前端缓存渲染内容的方法,避免重复加载数据渲染。根据用户提供的引用资料,我需要结合localStoragesessionStorageIndexedDB来分析。 首先,用户提到想存储渲染内容,这可能包括HTML结构、数据或两者。需要确定哪种存储方式适合同的场景。引用[1][2]比较了localStoragesessionStorage的区别,主要是作用域生命周期。localStorage适合持久化存储,而sessionStorage在会话期间有效。如果用户需要长期保存数据,比如离线访问,localStorage可能更合适,但要注意它的大小限制,通常是5MB左右,这可能存储大量渲染内容。 接下来是IndexedDB,引用[3]详细说明了它的特点,比如存储量大、支持二进制数据、异步操作等。如果渲染内容包含复杂结构或大量数据,比如图片或大型JSON对象,IndexedDB会更合适,因为它可以处理更大的数据量且会阻塞主线程。 然后,需要考虑如何具体实现。比如,对于localStorage,可以将渲染后的HTML字符串存储起来,下次加载时直接读取并插入到DOM中。但要注意,localStorage只能存字符串,所以可能需要序列化反序列化,比如使用JSON.stringifyJSON.parse。同时,数据更新时需要处理版本控制,避免旧数据导致问题。 对于IndexedDB,步骤可能更复杂,需要创建数据库对象仓库,使用事务来读写数据。过它可以存储更复杂的数据结构,甚至二进制文件适合存储完整的渲染状态或大量数据。异步特性也意味着会影响页面性能,但代码复杂度较高,可能需要使用库来简化操作,比如localForage。 另外,用户可能需要了解何时选择哪种技术。比如,小量数据且需要简单快速存取的话用localStorage;需要临时会话存储sessionStorage;大量数据或复杂操作则用IndexedDB。同时,要考虑数据的安全性同源策略,确保存储的数据会跨域泄露。 最后,用户可能需要知道如何结合这些技术与页面渲染流程。比如,在页面加载时检查本地是否有缓存,有的话直接使用缓存数据渲染,否则从服务器获取并存储。同时处理缓存过期或更新的问题,比如通过时间戳或版本号来验证数据有效性。 总结,需要分情况讨论存储方式的适用场景,给出具体实现步骤,并提醒注意事项,如数据格式、大小限制、异步处理等。可能还需要提到一些最佳实践,比如数据压缩、清理策略,以及使用Service Worker进行更高级的缓存管理。</think>以下是前端缓存渲染内容到本地的实现方法及技术选型建议,依据同场景可选用三种存储方案: ### 一、技术方案对比 | 存储方式 | 容量限制 | 数据生命周期 | 适用场景 | |----------------|---------------|----------------------------|--------------------------------------------------------------------------| | localStorage | 4-10MB | 永久有效(手动删除) | 静态页面结构、小型JSON数据、用户偏好设置 | | sessionStorage | 4-10MB | 当前会话(标签页关闭清除) | 临时渲染状态、表单草稿、单页面应用的临时缓存 | | IndexedDB | ≥250MB | 永久有效(手动删除) | 复杂DOM结构、二进制资源、大数据量渲染模板、需要事务操作的离线应用数据[^3] | ### 二、具体实现步骤 #### 方案1:localStorage存储 ```javascript // 存储渲染内容 function saveToLocalStorage(key, content) { const compressed = LZString.compress(JSON.stringify(content)); // 使用LZ压缩 localStorage.setItem(key, compressed); } // 读取并渲染 function loadFromLocalStorage(key) { const compressed = localStorage.getItem(key); if (compressed) { return JSON.parse(LZString.decompress(compressed)); } return null; } // 示例:存储列表渲染结果 const listHTML = document.getElementById('list').outerHTML; saveToLocalStorage('cachedList', {html: listHTML, timestamp: Date.now()}); ``` #### 方案2:IndexedDB存储(推荐大数据场景) ```javascript // 初始化数据库 const dbPromise = indexedDB.open('RenderCacheDB', 1); dbPromise.onupgradeneeded = (e) => { const db = e.target.result; if (!db.objectStoreNames.contains('renderedPages')) { db.createObjectStore('renderedPages', { keyPath: 'url' }); } }; // 存储方法 async function saveRenderedPage(url, content) { const db = await dbPromise; const tx = db.transaction('renderedPages', 'readwrite'); tx.objectStore('renderedPages').put({ url, content: new Blob([content], {type: 'text/html'}), expire: Date.now() + 86400000 // 24小时过期 }); return tx.complete; } // 读取方法 async function loadRenderedPage(url) { const db = await dbPromise; const tx = db.transaction('renderedPages'); const store = tx.objectStore('renderedPages'); const record = await store.get(url); if (record && record.expire > Date.now()) { return await record.content.text(); } return null; } ``` ### 三、混合缓存策略实践 1. **分层缓存架构** ```mermaid graph TD A[首次渲染] --> B{检查IndexedDB} B -->|命中缓存| C[直接渲染] B -->|未命中| D[请求服务器] D --> E[存储到IndexedDB] E --> F[备份到localStorage关键数据] ``` 2. **缓存更新机制** ```javascript window.addEventListener('online', async () => { const cachedData = await loadRenderedPage(currentURL); const serverData = await fetchData(); if (cachedData.version !== serverData.version) { showUpdateToast('检测到内容更新', { buttons: [ {text: '立即刷新', action: () => location.reload()}, {text: '稍后更新', action: () => markStale(cachedData)} ] }); } }); ``` ### 四、注意事项 1. **数据安全** - 敏感数据需加密存储:`CryptoJS.AES.encrypt(content, secretKey)` - 设置合理的缓存过期时间(建议≤24小时) - 同源策略下同子域名存储隔离[^2] 2. **性能优化** - 对HTML内容使用`DOMPurify.sanitize()`过滤XSS攻击 - 压缩存储数据(推荐LZ-String算法) - 对>1MB数据启用Web Worker进行后台存储[^3] 3. **异常处理** ```javascript try { localStorage.setItem(key, data); } catch (e) { if (e.name === 'QuotaExceededError') { clearExpiredCache(); // 使用LRU算法清理旧缓存 const keys = Object.keys(localStorage); while (keys.length > MAX_ITEMS) { localStorage.removeItem(keys.shift()); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值