Search code examples
vue.jsaxiosblobarraybufferpizzip

How can I use Axios to get an array of files to give to Pizzip to generate a .zip archive?


Here's the documentation for Pizzip for those unfamiliar: https://github.com/open-xml-templating/pizzip/tree/master/documentation

Also, I'm using VueJS (2), ES6, TS

I am trying to download individual files with Axios and then put them into a zip file using Pizzip.

async getFile(mat: Material): Promise<any> {
    try {
      const file = await axios.get(mat.url, { responseType: 'arraybuffer' })
      return file.data
    } catch (error) {
      console.log(error)
      return {}
    }
}

This is giving me the ArrayBuffer correctly. Then I have the following:

async getZip() {
    let zip = new pizzip()
    this.materials.forEach(async mat => {
        const record = await PortalService.getFile(mat)
        console.log(record) // logs the ArrayBuffer
        zip.file(mat.name, record)
    })
    let files = zip.generate({ type: 'blob' })
    return saveAs(files, `${this.request.id}-files.zip`)
}

Pizzip docs said that the data argument of zip.file() can be of type ArrayBuffer, but when I download the zip it's just an empty archive. I've tried using the optional options parameter for zip.file(), but nothing has worked. Thank you in advance.


Solution

  • The issue is probably to do with the async for-loop. The forEach method does not support async loop functions; it will just execute each iteration without waiting for the previous to finish. So basically you're generating the zip file before any files had been added to it.

    There are two ways you can handle this:

    1. Fire all requests concurrently and wait for them all to finish. This will be faster since it is sending the requests concurrently, but be careful not to spam hundreds of requests at once!
    await Promise.all(this.materials.map(async mat => {
      const record = await PortalService.getFile(mat)
      zip.file(mat.name, record)
    }))
    
    1. Fire each request sequentially, one at a time. Slower, but doesn't spam requests.
    for (const mat of this.materials) {
      const record = await PortalService.getFile(mat)
      zip.file(mat.name, record)
    }