来了,前端一键下载压缩包

本文最后更新于:2020年9月17日 上午

什么?你想一键下载图片压缩包

前提

什么?你想一键下载图片压缩包?这还真的是我接到需求后的第一句话。产品想把商品相册中的图片以压缩包的形式下载下来,于是我张口就来:后端开个接口呗。。。

一番调研之后,确认这个需求完全可以由前端独立实现,具体依赖以下两个库:

  • JSZip

    可以用非常简单的 API 来创建、读取和编辑 .zip压缩包文件。

  • FileSaver

    可以在 web客户端保存文件

实现思路

  • 首先利用 Image 下载图片,然后用 canvas 将图片导出 base64 形式
  • JSZip 创建文件夹,并将上一步获得的图片数据进行打包
  • 打包之后,使用 FileSaver 将打包好的 zip 保存到本地

具体代码

import JSZip from 'jszip';
import { saveAs } from 'file-saver';

export default (urls, zipName = '素材包') => {
	return new Promise((resolve, reject) => {
		const zip = new JSZip();
		// 创建文件夹
		const imgFolder = zip.folder(zipName);

		const imagePromises = urls.map((src) => getBase64Image(src));
		Promise.all(imagePromises)
			.then((results) => results.filter((result) => result.length > 0))
			.then((base64Images) => {
				console.table(base64Images);
				if (base64Images.length === 0) {
					reject();
					return;
				}
				for (let i = 0; i < base64Images.length; i++) {
					let base64Image = base64Images[i];
					// 去掉 bas64, 标志
					base64Image = base64Image.split('base64,')[1];
					// 添加文件到 folder 中
					imgFolder.file(`${i}.png`, base64Image, { base64: true });
				}
				// 打包
				zip
					.generateAsync({ type: 'blob' })
					.then((blob) => {
						// 导出下载
						saveAs(blob, `${zipName}.zip`);
						resolve();
					})
					.catch(() => {
						reject();
					});
			});
	});
};

function getBase64Image(src, width, height) {
	return new Promise((resolve) => {
		const image = new Image();
		image.crossOrigin = ''; // 设置跨域
		image.src = src;
		image.onload = () => {
			const canvas = document.createElement('canvas');
			canvas.width = width || image.width;
			canvas.height = height || image.height;

			const ctx = canvas.getContext('2d');
			ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
			resolve(canvas.toDataURL()); // 导出 base64
		};
		image.onerror = () => {
			console.log(`图片 ${src} 加载失败...`);
			resolve('');
		};
	});
}

特别要注意一下这句代码:

image.crossOrigin = ''; // 设置跨域

如果不设置的话,则 canvas 会报错:

Failed to execute ‘toDataURL’ on ’HTMLCanvasElement’: Tainted canvased may not be exported

H5 中,部分元素提供了支持 CORS 的属性,这些元素包括 <img>,<video>,<script>等,对应的属性名即 crossOrigin

属性值描述
use-credentials元素的跨域资源请求需要凭证标志设置
anonymous元素的跨域资源请求不需要凭证标志设置

我们这里虽然设置了 image.crossOrigin 为空字符串,但实际生效的是 anonymous,更详细的内容的可参考张鑫旭的解决 canvas 图片 getImageData,toDataURL 跨域问题这篇文章,我就不多说了。


 目录