往期精彩文章:
需求背景
这几天捣鼓一些小东西,需要实现这样一个功能:将页面指定的dom批量下载成压缩包。功能大致如下:
将每条评论信息转成图片,然后放到一个压缩包里下载下来。
压缩包内容
这篇文章就简单介绍下,我是如何实现的。
技术思路
要将dom转成图片,我们直接使用html2canvas就可以;要将所有图片放到压缩包里,我们首先获取到每个图片的
base64Data编码信息,最后借助jszip就可以实现。
技术方案
这篇文章就不适用vue了,用react写demo吧,函数是一样的。最后我会放上vue版本的完整代码。
使用云编译器进行调试
以下demo,大家可以直接在豆包云vscode编译器中调试,云编译器免配置任何环境,可以直接使用。
juejin.cn/post/738875... (见文末)
必要依赖安装
js
npm i file-saver
npm i html2canvas
npm i jszip
这三个包分别提供了在浏览器端实现文件保存、网页截图和 ZIP 文件处理的功能。
基础代码框架搭建
js
import { useRef, useState } from "react";
import html2canvas from "html2canvas";
import JSZip from "jszip";
import FileSaver from "file-saver";
function App() {
// 绑定dom 和vue的ref一样
const commentRefs = useRef([]);
// 图片导出
const exportImages = async () => {};
const mockData = [
{ name: "张三", age: 18 },
{ name: "李四", age: 18 },
{ name: "王五", age: 18 },
{ name: "石小石", age: 18 },
];
return (
<>
<div>
{mockData.map((item, index) => (
<div key={index} ref={(el) => (commentRefs.current[index] = el)}>
{/* 自定义的内容实现 */}
{item.name}
</div>
))}
</div>
<span onClick={() => exportImages()}>导出图片</span>
</>
);
}
export default App;
为了便于理解,上述代码只保留了核心逻辑。
dom转图片base64Data
首先,我们要借助html2canvas遍历所有dom生成base64Data数据
js
const exportImages = async () => {
// 循环遍历 commentRefs.current 数组中的每一个元素
for (let i = 0; i < commentRefs.current.length; i++) {
// 获取当前索引处的引用
const ref = commentRefs.current[i];
// 检查引用是否存在且索引 i 小于 commentRefs.current 的长度
if (ref && i < commentRefs.current.length) {
try {
// 使用 html2canvas 库将 ref 元素转换为 Canvas 元素
const canvas = await html2canvas(ref, { useCORS: true });
// 将 Canvas 元素转换为 base64 格式的 PNG 图片数据 URL
const dataUrl = canvas.toDataURL("image/png");
// 从数据 URL 中提取 base64 编码的图片数据部分
const base64Data = dataUrl.split(",")[1];
} catch (error) {
// 捕获任何导出图片时可能出现的错误,并输出到控制台
console.error("Error exporting the comment as an image:", error);
}
}
}
};
上述代码中,base64Data就是我们获取到的数据。
将base64Data写入压缩包
借助JSZip,我们可以把一些流数据进行打包,也可以将base64Data进行打包。
js
const exportImages = async () => {
// 创建一个新的 JSZip 实例
const zip = new JSZip();
for (let i = 0; i < commentRefs.current.length; i++) {
const ref = commentRefs.current[i];
if (ref && i < commentRefs.current.length) {
try {
const canvas = await html2canvas(ref, { useCORS: true });RL
const dataUrl = canvas.toDataURL("image/png");
const base64Data = dataUrl.split(",")[1];
// 将图片数据添加到 ZIP 文件中,使用 image{i+1}.png 作为文件名
zip.file(`image${i + 1}.png`, base64Data, { base64: true });
} catch (error) {
console.error("Error exporting the comment as an image:", error);
}
}
}
// 生成 ZIP 文件并将其作为 Blob 对象返回
const blob = await zip.generateAsync({ type: "blob" });
FileSaver.saveAs(blob, "images.zip");
};
这段代码扩展了之前的 exportImages
函数,添加了将生成的图片数据打包成 ZIP 文件的功能。关于JSZip的用法,大家可以看npm官网。这里我简单介绍下zip.file的用法。
zip.file
是 JSZip 库中用于向 ZIP 文件中添加文件的方法。它的基本用法是将数据添加到 ZIP 文件中,并指定文件的名称和数据。
基本语法
js
zip.file(fileName, data, options);
fileName
: 要添加到 ZIP 文件中的文件名称,可以包含路径,例如"images/image1.png"
。data
: 要添加的文件数据,可以是字符串、ArrayBuffer、Uint8Array 等,具体取决于你要添加的文件类型和数据格式。options
(可选): 是一个对象,可以包含以下选项:
-
base64
(boolean): 指定data
是否为 base64 编码,默认为false
。如果data
是 base64 编码的字符串,则需要设置为true
。
实现文件下载
要实现文件下载,我们一般借助FileSaver。FileSaver
是一个用于在浏览器中保存文件的 JavaScript 库。它通常与生成的文件内容(如通过其他库生成的 Blob 对象)一起使用。
基本用法
js
import FileSaver from "file-saver";
FileSaver.saveAs(blob, fileName);
blob
: 要保存的文件内容,通常是通过其他操作生成的 Blob 对象。fileName
: 下载文件的名称,可以包含文件扩展名,例如"example.txt"
。
因此,我们只需要在exportImages方法最后添加下面代码即可
js
FileSaver.saveAs(blob, "images.zip");
完整代码
react版本
js
import { useRef, useState } from "react";
import html2canvas from "html2canvas";
import JSZip from "jszip";
import FileSaver from "file-saver";
function App() {
const commentRefs = useRef([]);
const mockData = [
{ name: "张三", age: 18 },
{ name: "李四", age: 18 },
{ name: "王五", age: 18 },
{ name: "石小石", age: 18 },
];
const exportImages = async () => {
const zip = new JSZip();
for (let i = 0; i < commentRefs.current.length; i++) {
const ref = commentRefs.current[i];
if (ref && i < commentRefs.current.length) {
try {
const canvas = await html2canvas(ref, {
useCORS: true,
backgroundColor: "rgba(255, 255, 255, 0.6)",
});
const dataUrl = canvas.toDataURL("image/png");
const base64Data = dataUrl.split(",")[1];
zip.file(`image${i + 1}.png`, base64Data, { base64: true });
} catch (error) {
console.error("Error exporting the comment as an image:", error);
}
}
}
const blob = await zip.generateAsync({ type: "blob" });
FileSaver.saveAs(blob, "images.zip");
};
return (
<>
<div>
{mockData.map((item, index) => (
<div key={index} ref={(el) => (commentRefs.current[index] = el)}>
{/* 自定义的内容实现 */}
{item.name}
</div>
))}
</div>
<span onClick={() => exportImages()}>导出图片</span>
</>
);
}
export default App;
vue版本
js
<template>
<div>
<div v-for="(item, index) in mockData" :key="index" ref="setRef">
<!-- 自定义的内容实现 -->
{{ item.name }}
</div>
<span @click="exportImages">导出图片</span>
</div>
</template>
<script>
import { ref } from 'vue';
import html2canvas from 'html2canvas';
import JSZip from 'jszip';
import FileSaver from 'file-saver';
export default {
setup() {
// 使用 ref 创建响应式数据
const commentRefs = ref([]);
const mockData = [
{ name: '张三', age: 18 },
{ name: '李四', age: 18 },
{ name: '王五', age: 18 },
{ name: '石小石', age: 18 }
];
// 导出图片的方法
const exportImages = async () => {
const zip = new JSZip();
for (let i = 0; i < commentRefs.value.length; i++) {
const ref = commentRefs.value[i];
if (ref) {
try {
const canvas = await html2canvas(ref, {
useCORS: true,
backgroundColor: 'rgba(255, 255, 255, 0.6)'
});
const dataUrl = canvas.toDataURL('image/png');
const base64Data = dataUrl.split(',')[1];
zip.file(`image${i + 1}.png`, base64Data, { base64: true });
} catch (error) {
console.error('Error exporting the comment as an image:', error);
}
}
}
const blob = await zip.generateAsync({ type: 'blob' });
FileSaver.saveAs(blob, 'images.zip');
};
// 将引用存入 commentRefs 数组中
const setRef = (el) => {
commentRefs.value.push(el);
};
return {
mockData,
exportImages,
setRef
};
}
};
</script>
总结
这篇文章介绍了如何使用html2canvs
将多个dom转成base64Data图片数据
,然后借助jszip
进行打包,最后通过file-saver
下载到本地的简单技术方案。
借助这一套方案,其实也可以实现纯前端将网页转成pdf,最后下载成压缩包。
这篇文章中,没有做demo图片中的进度条功能,这个很简单,大家可以结合业务自行实现。