Justin Young
Justin Young

Reputation: 2453

Streaming a zip download from cloud functions

I have a firebase cloud function that uses express to streams a zip file of images to the client. When I test the cloud function locally it works fine. When I upload to firebase I get this error:

Error: Can't set headers after they are sent.

What could be causing this error? Memory limit?

export const zipFiles = async(name, params, response) => {


  const zip = archiver('zip', {zlib: { level: 9 }});


  const [files] = await storage.bucket(bucketName).getFiles({prefix:`${params.agent}/${params.id}/deliverables`});

  if(files.length){
    response.attachment(`${name}.zip`);
    response.setHeader('Content-Type', 'application/zip');
    response.setHeader('Access-Control-Allow-Origin', '*')

    zip.pipe(output);

    response.on('close', function() { 
      return output.send('OK').end(); // <--this is the line that fails
  });

  files.forEach((file, i) => {

    const reader = storage.bucket(bucketName).file(file.name).createReadStream();
    zip.append(reader, {name: `${name}-${i+1}.jpg`});

  });
  zip.finalize();
}else{
  output.status(404).send('Not Found'); 
}

Upvotes: 1

Views: 1614

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317362

What Frank said in comments is true. You need to decide all your headers, including the HTTP status response, before you start sending any of the content body.

If you intend to express that you're sending a successful response, simply say output.status(200) in the same way that you did for your 404 error. Do that up front. When you're piping a response, you don't need to do anything to close the response in the end. When the pipe is done, the response will automatically be flushed and finalized. You're only supposed to call end() when you want to bail out early without sending a response at all.

Bear in mind that Cloud Functions only supports a maximum payload of 10MB (read more about limits), so if you're trying to zip up more than that total, it won't work. In fact, there is no "streaming" or chunked responses at all. The entire payload is being built in memory and transferred out as a unit.

Upvotes: 1

Related Questions