背景:一方面,部分系统对文件的私密性和安全性要求较高,实现前端加密打包,服务端不存储密码 ,下载时手动输入密钥并解压文件。另一方面,传输压缩包到客户端,节约了带宽,节约了传输时间。
使用的库: zip.js
- Support of the Zip64 format
- Support of WinZIP AES and PKWare ZipCrypto encryption
- Support of simultaneous reads and writes to one or more zip files
- Integrated worker pool manager
- No dependencies
npm install @zip.js/zip.js
import * as zip from "@zip.js/zip.js"; //ES6 module
const zip = require("@zip.js/zip.js"); // or CommonJS module
以下为vue的实现方案:
import * as zip from '@zip.js/zip.js'
...
...
methods: {
async handleZipFile () {
const controller = new AbortController()
const signal = controller.signal
var zipWriter = new zip.ZipWriter(new zip.BlobWriter('application/zip'))
await Promise.all(_.map(this.files, async (file) => {
await zipWriter.add(file.name, new zip.BlobReader(file.raw), {
bufferedWrite: true,
password: this.password,
signal,
zipCrypto: true,
onprogress: (index, max) => {
const percent = parseInt(index / max * 100 || 0)
this.loadingText = `正在打包文件${file.name},进度为${percent}%`
}
})
}))
let [err, data] = await callAsync(zipWriter.close())
if (data) {
await this.uploadFile(new File([data], this.name + '.zip'))
}
},
async uploadFile (file) {
let formdata = new FormData()
formdata.append('file', file)
formdata.append('type', this.type) // 其他字段
let [err, res] = await callAsync(axios({
url: `/api/upload`,
method: 'post',
data: formdata,
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 0,
onUploadProgress: progressEvent => {
let percent = (progressEvent.loaded / progressEvent.total * 100 | 0)
this.loadingText = `正在上传文件${file.name},进度为${percent}%`
}
}))
if (err) {
if (axios.isCancel(err)) {
this.$message.success('已取消上传')
} else {
this.$message.error('文件上传失败' + err)
}
} else {
this.loadingText = ''
}
}
}
methods: {
async getZipFiles (password, fileUrl, projectId) {
try {
const reader = new zip.ZipReader(new zip.HttpReader(fileUrl, {}), {
password: password
})
const entries: any = await reader.getEntries()
await reader.close()
return [null, { password: password, files: entries }]
} catch (err) {
return [{ msg: err.toString() }, null]
}
}
}
methods: {
async downloadUnZipFile (file) {
const getURL = async (entry, options) => {
return URL.createObjectURL(await entry.getData(new zip.BlobWriter(), options))
}
const controller = new AbortController()
const signal = controller.signal
await getURL(file, {
password: this.password,
onprogress: (index, max) => {
console.log(index, max)
},
onerror: (err) => {
this.$message.error(err.toString())
},
signal
}).then((blobURL) => {
// 下载文件
const a = document.createElement('a')
a.href = blobURL
a.download = file.filename
a.target = '_blank'
const clickEvent = new MouseEvent('click')
a.dispatchEvent(clickEvent)
}).catch((err) => {
this.$message.error(err.toString())
})
}
}
实际使用中,如果压缩文件很大,如上G的文件,可能会出现错误。