3

I'm generating a compressed file based on JSON data. Currently, when I generate a TAR file, my API works just fine. Here's my code:

app.post('/', (req, res) => {
  const filename = 'export.tar';

  const archive = archiver('tar', {});

  archive.on('warning', (err) => {
    console.log(`WARN -> ${err}`);
  });

  archive.on('error', (err) => {
    console.log(`ERROR -> ${err}`);
  });

  const files = req.body.files || [];
  for (const file of files) {
    archive.append(file.content, { name: file.name });
    console.log(`Appending ${file.name} file: ${JSON.stringify(file, null, 2)}`);
  }

  try {
    if (files.length > 0) {
      archive.pipe(res);
      archive.finalize();
      return res.attachment(filename);
    } else {
      return res.send({ error: 'No files to be downloaded' });
    }
  } catch (e) {
    return res.send({ error: e.toString() });
  }
});

And here is some sample JSON I pass through:

{
  "title": "Sample Title",
  "files": [
    {
      "name": "index.html",
      "content": "<p>Hello, World!</p>"
    },
    {
      "name": "README.md",
      "content": "# Hello, World!"
    }
  ]
}

But, when I change this to generate a ZIP file, I get errors like 21 "Is a directory" or 2 "No such file or directory."

The code I changed is:

  const filename = 'export.zip';
  const archive = archiver('zip', {
    zlib: { level: 9 },
  });

I've tried looking at other questions and issues, but haven't had any luck when making store: true or moving finalize() or making forceZip64: true. What can I change to make ZIP files work correctly?

Cassidy
  • 3,328
  • 5
  • 39
  • 76
  • Not sure how much help this is, but on my environment (Windows 10, build 17763, Node v10.15.1, your code works perfectly well.. I'm testing with Postman, the export.zip file opens as expected. – Terry Lennox Mar 25 '19 at 09:56
  • 1
    @TerryLennox thank you for letting me know! That is somewhat helpful; lets me know that it's a Mac thing I have to look out for. – Cassidy Mar 25 '19 at 17:08

1 Answers1

1

This did the trick! By creating the output with createWriteStream and then piping the archive to that, we are able to download a zip that works perfectly.

app.post('/', (req, res) => {
  const filename = 'export.zip';

  const archive = archiver('zip', {});

  let output = fs.createWriteStream(filename);

  archive.on('warning', err => {
    console.log(`WARN -> ${err}`);
  });

  archive.on('error', err => {
    console.log(`ERROR -> ${err}`);
  });

  output.on('end', function() {
    console.log('Data has been drained');
  });
  archive.pipe(output);

  const files = req.body.files || [];
  for (const file of files) {
    archive.append(file.content, { name: file.name });
    console.log(`Appending ${file.name} file: ${JSON.stringify(file, null, 2)}`);
  }

  try {
    if (files.length > 0) {
      archive.finalize();
      res.download(filename);
    } else {
      return res.send({ error: 'No files to be downloaded' });
    }
  } catch (e) {
    return res.send({ error: e.toString() });
  }
});
Cassidy
  • 3,328
  • 5
  • 39
  • 76