0

How to upload file using FormData with multer in Node.js? I am getting undefined in req.file and { } in req.body.

Here is the HTML code:

<input type="file" name="file" id="file" onchange="angular.element(this).scope().uploadFile(this.files)">

Here is the JavaScript code:

$scope.uploadFile = function (files) {
        var fd = new FormData();
        //Take the first selected file
        console.log("files", files);
        fd.append("file", files[0]);
        $http({
            method: 'POST',
            url: 'http://localhost:5000/upload',
            headers: {
                'Content-Type': undefined
            },
            data: fd

        }).then(function (response) {
        }, function (error) {
        });

    };

Here is the server side code:

var http = require('http');
var express = require('express');
var cors = require('cors');
var multer = require('multer');
var bodyParser = require('body-parser');
var upload = multer({ dest: 'public' });
var app = express();

app.use(cors());
app.use(bodyParser.json())

app.use(express.static('public'))

app.get('/', function (req, res) {
  res.sendStatus(200)
})


app.post('/upload', upload.single('file'), function (req, res) {

  console.log("req.file", req.file); //req.file undefined
  console.log("req.body", req.body); //req.body {}

});

app.listen(5000, function () {
  console.log("server listening on port 5000 ");

})
bad_coder
  • 11,289
  • 20
  • 44
  • 72

3 Answers3

0

The backend code looks ok, however you need to set the correct content-type on the client-side.

By providing the form-data as its data argument you should not actually have to define the content-type explictily. So you can simply do:

$http('http://localhost:5000/upload', fd)

If you want to specify the content-type explicitly, you can use:

$http('http://localhost:5000/upload', fd, {
    headers: {
        'content-type': 'multipart/form-data'
    }
})
eol
  • 23,236
  • 5
  • 46
  • 64
0

Try using ajax for executing the POST request.

var form = new FormData();
form.append("file", files[0], "/path/to/file");

var settings = {
  "url": "http://localhost:5000/upload",
  "method": "POST",
  "timeout": 0,
  "processData": false,
  "mimeType": "multipart/form-data",
  "contentType": false,
  "data": form
};

$.ajax(settings).done(function (response) {
  console.log(response);
});
lousybear
  • 453
  • 3
  • 12
0

I am trying to set up a minimalistic server using Koa and lowdb; I am also writing everything on typescript and using the experimental feature to work with modules in (nodejs) koa.

I share firstly my .tsconfig

{
  "compilerOptions": {
    "target": "es2017",
    "module": "ESNext",
    "lib": [
      "es6","DOM"
    ] ,
    "allowJs": true,
    "outDir": "build",
    "rootDir": "src" ,
    "strict": true ,
    "noImplicitAny": true,
    "moduleResolution": "node",                 
    "esModuleInterop": true,
   
    /* Advanced Options */
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }

Did manage to set up the server but got in a lot of trouble when I wanted to add on Multer and effectively, which library to chose from to handle the incoming data.

on this POST there is a good discussion about the different libraries available at the end I chose to use async-busboy [...] seems to be more up to date, and of course, bring on its own types.

koa is known to treat everything as middleware, so wrappers on async-await are useful!

import multer from '@koa/multer';
import koaStatic from 'koa-static';
import path from 'path';
import asyncBusboy from 'async-busboy';
import multer from '@koa/multer';
import koaStatic from 'koa-static';
import path from 'path';
import asyncBusboy from 'async-busboy';


const router = new Router();
const app = new Koa();

const corsOpt = {
  origin: /* process.env.CORS_ALLOW_ORIGIN ||  */ '*', // this work well to configure origin url in the server
  methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'], // to works well with web app, OPTIONS is required
  allowedHeaders: ['Content-Type', 'Authorization'], // allow json and token in the headers
};
app.use(cors(corsOpt));
app.use(logger());
app.use(koaBody());

app.use(koaStatic(__dirname + './static/uploads'));

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, './uploads');
  },
  filename: function (req, file, cb) {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
    cb(null, file.fieldname + '-' + uniqueSuffix + '.jpg');
  },
});
const upload = multer({ storage });

app.use(upload.any());

app.use(async function (ctx: KoaContext, next: () => any) {
  // ignore non-POSTs
  if ('POST' != ctx.method) return await next();
  const { files, fields } = await asyncBusboy(ctx.req);
  console.log(files);
  let err = await upload
    .single('file')(ctx as any, next)
    .then((res: any) => res)
    .catch((err: any) => err);
  if (err) {
    ctx.body = {
      code: 0,
      msg: err.message,
    };
  } else {
    ctx.body = {
      code: 1,
      data: files,
    };
  }
});

// add a route for uploading multiple files
router.post('/uploader', upload.fields([]), (ctx: KoaContext) => {
  console.log('ctx.request.files', ctx.request.files);
  console.log('ctx.files', ctx);
  console.log('ctx.request.body', ctx.request.body);
  ctx.body = 'done';
});

// REST OF CODE ...

app.use(router.routes()).use(router.allowedMethods());

app.listen(4040);
console.log('server running ... http://localhost:4040');

I am still learning, or better said decypher all typescript secrets by doing such experiments. Now I do have a conflict as mutler is doing its own form-data (I assumed by reading from others posts) So far it is a solution to get the file from ctx.req.file via async-await, But I do still struggling with the types.

You can check the whole project here repo

Any help on the types will be great.

Hope it helps to further this discussion now with typescript.

  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. – Jon Weers Aug 30 '21 at 18:03