Search code examples
node.jsangularexpressmultipartform-datamulter

Uploading files to backend server on a POST request through Multer, Node and Express


I am trying to send a from from my local system to the backend server via POST request but I am unable to do that every-time it goes with a --binary-data instead of going with a --form.

Here is the sample curl which I want to construct using Node.js:

curl --location --request POST 'some-endpoint' \
--form 'file=@"/Users/name/Desktop/sample.csv"'

Here is the component.html:

<form (ngSubmit)="handleCsvClick()" encrypt="multipart/form-data">
 <input
  type="file"
  name="file-input"
  #fileInput
  [disabled]="requestInProgess"
  (change)="handleFilePick()"
/>
<button
class="upload-btn btn-reset"
type="submit"
>
Upload CSV File
</button>
</form>

Here is the component.ts:

 handleCsvClick(){
    const file = this.fileInputRef.nativeElement.files[0];
    const form = new FormData();
    form.append("file", file);
    const endpoint = `/api/upload-csv`;
    this.http.post(endpoint,form).subscribe((data) => {
      console.log(data);
    });
  }

Node.js

Router file:

const path = require('path');
const express = require("express");
const cookieParser = require("cookie-parser");
const multer = require('multer');
const router = express.Router();
const uuidv4 = require('uuid/v4');
const advisoryController = require("../controllers/advisoryController");
const { UPLOADS_PATH } = require('../config/environment');
const storage = multer.diskStorage({
  destination: UPLOADS_PATH,
  filename: function(_, file, cb) {
    const fileExt = path.extname(file.originalname);
    cb(null, `_csvUploadFile_${uuidv4()}${fileExt}`);
  },
});
const upload = multer({ storage });

router.post("/upload-csv", upload.single("file"), (req, res) => {
  advisoryController
    .uploadCSV(req.file)
    .then(response => res.send(response.data))
    .catch(error => {
      res.status(error.status || 500).send("Something went wrong");
      console.log(error);
    });
});

Controller:

const axios = require("axios");
const { advisoryList } = require("../config/apiConfig");
const fs = require('fs');
const request = require('request');
const FormData = require('form-data');

function uploadCSV(file) {
  let requestUrl = advisoryList.uploadCSV.url;
  const { path } = file;
  const fileUpload = fs.createReadStream(path);
  return new Promise((resolve, reject) => {
    axios
    .post(requestUrl, fileUpload, {headers: {
      'Content-Type': 'multipart/form-data'
    }})
    .then(response => {
      resolve(response.data);
    })
    .catch(error => {
      reject(error)
      console.log("CURL: ", error.request.toCurl());
    });
  })
}

The curl which is getting constructed after all this is coming as --data-binary instead of --form:

curl 'someendpoint' -H 'accept: application/json, text/plain, */*' -H 'content-type: multipart/form-data' -H 'user-agent: axios/0.18.0' --data-binary $'userId,name,age,city\n29298914,vish,30,Bangalore\r\n0\r\n\r\n' --compressed

Instead of:

curl --location --request POST 'some-endpoint' \
--form 'file=@"/Users/name/Desktop/sample.csv"'

Any help would be appreciated. Thanks in advance.


Solution

  • To send a file with axios in Node.js you need to:

    1. Create a form using form-data library
    2. Set the request headers from the form with getHeaders()

    Change your code to this:

    const form = new FormData();
    form.append('file', fileUpload, path); // 3rd argument is the file name
    
    // Send other fields along with the file
    form.append('title', 'Q1 2021');
    form.append('description', 'An utterly interesting report');
    
    // Get form headers to pass on to axios
    const formHeaders = form.getHeaders();
    
    // Sending a file with axios
    axios.post(requestUrl, form, { headers: { ...formHeaders } });
    

    Your question inspired me to turn this answer into an article — Send a File With Axios in Node.js. It covers a few common pitfalls and you'll learn how to send files that are stored as a Buffer or coming from a Stream.