背景
因项目中有批量下载文件的需求,文件类型包含 jpg/png/pdf/ofd/xml 等多种格式,因下载接口只支持单个文件下载,每个文件下载都需要创建一个 a 标签来实现下载,下载的文件也都是单个分开的,交互不是很友好,博主想到能否实现将下载的文件生成一个压缩包下载到本地,所以找到了以下的解决方案。
一、依赖包的版本及安装
jszip:3.9.1
file-saver:2.0.5
npm i jszip@3.9.1 file-saver@2.0.5
提示:依赖包的版本差异不大,可能引入方式会有变化,若版本不一致具体可参考官方提供的文档
二、封装工具函数
import JSZip from 'jszip'
import FileSaver from 'file-saver'
/**
* 批量下载文件并压缩
* @param {Array} fileIdList 文件资源 id 数组
* @param {String} zipName 压缩包的名称
*/
export async function batchDownloadFile(fileIdList, zipName = '未命名.zip') {
if (!fileIdList.length) {
console.log('选中的数据中不包含文件信息')
return
}
const jszip = new JSZip()
let promises = []
await fileIdList.forEach(fileId => {
// 调请求获取文件资源
const promise = $vm.$http.get('http://www.test.com/downloadFile', { fileId }, {
responseType: 'arraybuffer',
responseAll: true, // 自定义封装的请求方式,重点是拿到请求的所有响应信息
headers: { token: '123' }
}).then(response => {
const { headers, data } = response
const fileName = headers['content-disposition'] // 和服务端协定的文件名响应头存放属性名
jszip.file(fileName, data, { binary: true }) // 对单个下载的文件压缩
})
promises.push(promise)
})
// 批量调用下载文件请求
Promise.all(promises).then(() => {
jszip.generateAsync({ type: 'blob' }).then(content => {
FileSaver.saveAs(content, zipName)
})
})
}
JSZip 的 generateAsync 方法用于生成 ZIP 文件的内容。它接受两个参数:
- options:一个对象,用于配置生成的 ZIP 文件。这个对象可以包含以下属性:
-
type:生成的数据类型。可以是 “base64”、“binarystring”、“uint8array”、“arraybuffer”、“blob”、“nodebuffer” 中的一种。默认值是 “base64”。
-
compression:压缩算法。可以是 “STORE”(无压缩)或 “DEFLATE”(使用 zlib 进行压缩)。默认值是 “STORE”。
-
compressionOptions:一个对象,用于配置压缩算法。对于 “DEFLATE” 压缩,可以包含 level 属性来设置压缩级别。
-
mime:生成的数据的 MIME 类型。默认值是 “application/zip”。
-
platform:生成的 ZIP 文件的平台。可以是 “DOS” 或 “UNIX”。默认值是 “DOS”。
- updateCallback:一个函数,会在生成 ZIP 文件的过程中被周期性地调用。这个函数接受一个参数,是一个表示进度的数字(从 0 到 1)。
三、简单的原理介绍
1. jszip
JSZip 是一个 JavaScript 库,用于创建、读取和编辑 .zip 文件。它使用 JavaScript 实现了 ZIP 文件格式的相关规范,可在浏览器和 Node.js 环境中使用。
以下是 JSZip 的一些主要实现原理:
-
ZIP 文件格式:一个 ZIP 文件包含了一个或多个被压缩的文件,以及描述这些文件的元数据。JSZip 实现了 ZIP 文件格式的相关规范,包括文件的压缩和解压缩、元数据的读取和写入等。
-
数据压缩:JSZip 支持多种数据压缩算法,包括 STORE(无压缩)和 DEFLATE。DEFLATE 是一种常用的数据压缩算法,它结合了 LZ77 和 Huffman 编码。JSZip 使用 JavaScript 实现了这些压缩算法。
-
数据结构:JSZip 使用 JavaScript 对象来表示 ZIP 文件和其中的文件。每个 JSZip 对象代表一个 ZIP 文件,每个 JSZipObject 对象代表一个文件。你可以使用 JSZip 提供的 API 来操作这些对象,例如添加和删除文件、读取和修改文件内容等。
-
异步处理:JSZip 支持异步 API,这意味着你可以在处理大文件或多个文件时避免阻塞主线程。JSZip 使用 Promise 来实现异步处理。
-
兼容性:JSZip 可以在多种环境中使用,包括现代浏览器、旧版浏览器和 Node.js。为了实现这种兼容性,JSZip 使用了一些兼容性技术,例如使用 Blob 和 ArrayBuffer 来处理二进制数据、使用 polyfill 来支持旧版浏览器等。
2.file-saver
FileSaver.js 是一个 JavaScript 库,它提供了一个在客户端保存文件的解决方案,无论它是从 JavaScript 生成的数据还是从远程服务器获取的数据。
以下是 FileSaver.js 的一些主要实现原理:
-
创建 Blob 对象:FileSaver.js 首先会创建一个 Blob 对象,这个对象包含了要保存的数据。Blob 对象是一个可以存储大量二进制数据的对象,它可以用来表示文件的内容。
-
创建 Object URL:然后,FileSaver.js 会使用 URL.createObjectURL 方法创建一个表示 Blob 对象的 Object URL。这个 URL 可以被浏览器识别,并用来下载 Blob 对象的内容。
-
创建并点击 a 标签:接着,FileSaver.js 会创建一个 a 标签,设置其 href 属性为 Object URL,设置其 download 属性为要保存的文件名,然后模拟点击这个 a 标签。这会触发浏览器的下载行为,将 Blob 对象的内容保存为一个文件。
在不支持 download 属性或者 Blob 对象的浏览器中,FileSaver.js 可能会退化为打开文件而不是下载文件
。 -
回收资源:最后,FileSaver.js 会使用 URL.revokeObjectURL 方法回收 Object URL。这是因为每个 Object URL 都会占用一些内存,当它不再需要时,应该尽快回收。
四、扩展 - FileSaver 模拟实现
/**
* 下载文件
* @param {} data 创建 Blob 对象的数据
* @param {String} filename 文件名
* @param {String} mime 文件 MIME 类型
*/
export const fileSaveAs = (data, filename, mime) => {
let blobData = [data]
let blob = new Blob(blobData, {type: mime || 'application/zip'})
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename)
} else {
let blobURL = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(blob) : window.webkitURL.createObjectURL(blob)
let tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', filename)
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
document.body.appendChild(tempLink)
tempLink.click()
setTimeout(() => {
document.body.removeChild(tempLink)
window.URL.revokeObjectURL(blobURL)
}, 200)
}
}
- FileSaver.saveAs(content, zipName) 可直接替换成 fileSaveAs(content, zipName)