Reputation: 21
I am using archiver lib to zip files on fly and send the zip stream directly to the client browser, it's working but, one problem - it's not showing progress, as I am not providing the content- length. How can I calculate content-length before hand ? In my case the length of files are known, but the final archive's size is more than the sum of all files. It is so because there are folders in my archive. So how can I calculate folder size separately? so that I can use - FilesSize + FoldesrSize = Contentlength. I have chosen compression as Store.
Upvotes: 2
Views: 243
Reputation: 1
I came across this question while trying to solve the same thing, it's a bit late but I hope this helps someone.
I basically solved it by first simulating the process by streaming to the OS temporary directory, I got the content length and then streamed out the destination folder with the content length to the client. Code snippets in Typescript below:
// 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)
}
Upvotes: 0