node.jsjsonexpressapi-designarchiverjs

How can I get a compressed file with archiver from a POST request?


I am building a NodeJS API with Express where when you make a POST, it generates a TAR file based on the body of the request.

Problem:

When the endpoint is a POST, I have access to the body of the request, and can seemingly make things with it. But, I can’t see/use/test a compressed file from that (as far as I can tell).

When the endpoint is a GET, I don’t have access to the body of the request (as far as I can tell), but I can query the URL in the browser and get the compressed file.

Basically I want to solve one of the “as far as I can tell's. This is my relevant code so far:

const fs = require('fs');
const serverless = require('serverless-http');
const archiver = require('archiver');
const express = require('express');
const app = express();
const util = require('util');

app.use(express.json());


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

  var output = fs.createWriteStream('/tmp/' + filename);

  output.on('close', function() {
    res.download('/tmp/' + filename, filename);
  });

  var archive = archiver('tar');

  archive.pipe(output);

  // This part does not work when this is a GET request.
  // The log works perfectly in a POST request, but I can't get the TAR file from the command line.
  res.req.body.files.forEach(file => {
    archive.append(file.content, { name: file.name });
    console.log(`Appending ${file.name} file: ${JSON.stringify(file, null, 2)}`);
  });

  // This part is dummy data that works with a GET request when I go to the URL in the browser
  archive.append(
    "<h1>Hello, World!</h1>",
    { name: 'index.html' }
  );

  archive.finalize();
});

Sample JSON body data that I send to this:

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

I’m just supposed to send JSON and get a TAR based on the SON. Is POST the wrong method for this? If I use GET, what should change so I can use that JSON data? Is there a way to "daisy chain" requests (that seems unclean, but maybe the solution)?


Solution

  • Try this:

    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() });
      }
    });