我正在使用 archiver lib 动态压缩文件并将 zip 流直接发送到客户端浏览器,它可以工作,但是有一个问题 - 它没有显示进度,因为我没有提供内容长度。如何提前计算内容长度?就我而言,文件的长度是已知的,但最终存档的大小大于所有文件的总和。之所以如此,是因为我的存档中有文件夹。那么如何单独计算文件夹大小呢?这样我就可以使用 - FilesSize + FoldesrSize = Contentlength。我选择压缩作为存储。
我在尝试解决同样的问题时遇到了这个问题,有点晚了,但我希望这对某人有帮助。
我基本上解决了这个问题,首先通过流式传输到操作系统临时目录来模拟过程,我获取了内容长度,然后将包含内容长度的目标文件夹流式传输到客户端。下面是 Typescript 中的代码片段:
// imports
import { tmpdir } from 'os'
import { createReadStream, createWriteStream } from 'fs'
import { Readable } from 'stream'
import { Response } from 'express'
import archiver from 'archiver'
// object to store the simulation result
class ZipSimulationResult {
simulatedSize: number
destination: string
}
// in this case, I pass an array of Readable streams,
// but you can easily create a Readable stream from your files instead
async function simulateZipStream(
streamArr: Readable[]
): Promise<ZipSimulationResult> {
return new Promise((resolve, reject) => {
const tempDir = tmpdir()
const destination = `${tempDir}/${Date.now()}.zip`
const destinationStream = createWriteStream(destination)
const archive = archiver('zip')
archive.pipe(destinationStream)
for (let i = 0; i < streamArr.length; i++) {
// you probably need the file extension as well for the
// file name, edit the code according to your needs
archive.append(streamArr[i], { name: `file_${i}` })
}
archive.on('error', function (err) {
console.error('Error while simulating zip: ', err)
reject(err)
})
archive.on('end', () => {
console.log('simulated archive size: ' + archive.pointer())
const result = new ZipSimulationResult()
result.destination = destination
// archive.pointer() returns the totalBytes wrote by archiver
result.simulatedSize = archive.pointer()
resolve(result)
})
archive.finalize()
})
}
/// To use
/// Re-write this to suit your use case
async function zipAndStream(
zipFileName: string,
streamArr: Readable[],
res: Response
) {
const simulationResult = await simulateZipStream(streamArr)
const zipReadStream = createReadStream(simulationResult.destination)
res.setHeader('content-length', simulationResult.simulatedSize)
res.setHeader('content-type', 'application/zip')
res.setHeader(
'content-disposition',
`attachment; filename="${zipFileName}.zip"`
)
// you could also listen to the 'error' event on 'res'
zipReadStream.pipe(res)
}