220

I'm working on uploading a file to my app using the multer npm module.

The multer function I have defined is to allow a single file uploaded to the file system. Everything works during run time; the issue is after I upload the file I get an error below. Any advice appreciated on where to look.

Error:

Unexpected field

Error: Unexpected field
    at makeError (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\lib\make-error.js:12:13)
    at wrappedFileFilter (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\index.js:39:19)
    at Busboy.<anonymous> (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\lib\make-middleware.js:97:7)
    at Busboy.emit (events.js:118:17)
    at Busboy.emit (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\node_modules\busboy\lib\main.js:31:35)
    at PartStream.<anonymous> (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\node_modules\busboy\lib\types\multipart.js:205:13)
    at PartStream.emit (events.js:107:17)
    at HeaderParser.<anonymous> (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\node_modules\busboy\node_modules\dicer\lib\Dicer.js:51:16)
    at HeaderParser.emit (events.js:107:17)
    at HeaderParser._finish (c:\Users\Dev\WebstormProjects\Crunch\node_modules\multer\node_modules\busboy\node_modules\dicer\lib\HeaderParser.js:70:8) 

app.js

var multer = require('multer');
var app = express();
var fs = require('fs');

//. . . 

var upload = multer({ dest: 'upload/'});
var type = upload.single('file');

app.post('/upload', type, function (req,res) {
  var tmp_path = req.files.recfile.path;
  var target_path = 'uploads/' + req.files.recfile.name;
fs.readFile(tmp_path, function(err, data)
{
  fs.writeFile(target_path, data, function (err)
  {
    res.render('complete');
  })
});

Index.hbs

<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name='recfile' placeholder="Select file"/>
    <br/>
    <button>Upload</button>
</form>

#Package.json
  "dependencies": {
    "body-parser": "~1.13.2",
    "cookie-parser": "~1.3.5",
    "debug": "~2.2.0",
    "easy-zip": "0.0.4",
    "express": "~4.13.1",
    "hbs": "~3.1.0",
    "less-middleware": "1.0.x",
    "morgan": "~1.6.1",
    "multer": "~1.0.0",
    "serve-favicon": "~2.3.0"
  }
}
Sethe23
  • 2,552
  • 4
  • 18
  • 18

14 Answers14

330

The <NAME> you use in multer's upload.single(<NAME>) function must be the same as the one you use in <input type="file" name="<NAME>" ...>.

So you need to change

var type = upload.single('file')

to

var type = upload.single('recfile')

in you app.js

Halo
  • 1,730
  • 1
  • 8
  • 31
Vincent Schöttke
  • 4,416
  • 2
  • 12
  • 12
  • 14
    It would help if they put this in the readme instead of filling it with 'avatar'. – hugos Jan 02 '17 at 21:07
  • 2
    But we still need to avoid the exception in case of misuse.. how to catch this exception? – syberkitten May 29 '17 at 15:39
  • 1
    Just for reference, if you're using curl, and the command looks like this:curl -v -F upload=@/myfile.txt http://localhost:3000/upload Then the value for upload.single is "upload" – chrismarx Dec 01 '17 at 19:26
  • You are right bro – Rajib Jan 02 '23 at 20:00
  • @Vincent is there any why we can pass error message to the client to tell that the field name you are providing is not same as you specified in upload.single('field name') ? – Kashif Ali Jan 17 '23 at 15:31
203

We have to make sure the type= file with name attribute should be same as the parameter name passed in upload.single('attr')

var multer  = require('multer');
var upload = multer({ dest: 'upload/'});
var fs = require('fs');

/** Permissible loading a single file, 
    the value of the attribute "name" in the form of "recfile". **/
var type = upload.single('recfile');

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

  /** When using the "single"
      data come in "req.file" regardless of the attribute "name". **/
  var tmp_path = req.file.path;

  /** The original name of the uploaded file
      stored in the variable "originalname". **/
  var target_path = 'uploads/' + req.file.originalname;

  /** A better way to copy the uploaded file. **/
  var src = fs.createReadStream(tmp_path);
  var dest = fs.createWriteStream(target_path);
  src.pipe(dest);
  src.on('end', function() { res.render('complete'); });
  src.on('error', function(err) { res.render('error'); });

});
Thamaraiselvam
  • 6,961
  • 8
  • 45
  • 71
stdob--
  • 28,222
  • 5
  • 58
  • 73
  • 97
    Would you mind explaining why this works and what's different? —_____— – IIllIIll Jun 01 '16 at 19:57
  • 8
    Works perfectly like charm.We have to make sure the type= file with name attribute should be same as the parameter name passed in upload.single('attr') – Ramki Jul 26 '16 at 09:44
  • 1
    Mine case it's not working. I am facing same issue. But in my windows machine code is working. I am having issues with my MAC.? Can anyone help with me this? – HaRdik Kaji Aug 02 '16 at 11:12
  • 7
    The name attribute of the type="file" in html should match the upload.single('name') in server code. – Prasanth Jaya Sep 10 '16 at 09:28
  • How to set the client request for a multiple files upload? The 'files' field is empty. – 吳強福 Jun 11 '17 at 11:30
  • Note: @stdob seems like it want tmp file & dest file in same directory. Can't do an edit for this but take care: path are differents in the code (`upload/` and `uploads/`) – Apolo Jul 18 '17 at 17:21
  • Mine works in osx but it leaves behind the temporary file which doesn't have an extension name and appears in some kinda of temporary file name "abd71003bbe62925b961681ce134d08c" in the same directory as final file – Paul Morris Apr 23 '20 at 21:30
  • Thanks for the solution. It worked perfectly for me. I am left with only one issue which is currently my directory structure is like this: Inside App folder there are two more directories one for frontend and another one for backend. so currently the image is being saved inside the backend folder but I want to save it to the frontend folder so that I can reference it in my img tag. Can you share the way how it can be done. – Sumit May 23 '20 at 14:45
  • @stdob In my case when uploading multiple files `upload.array('photos', 10)` , I noticed **something strange**, First, ensured that `photos` was name that was being sent. but still received this error. Next I went inside `node_modules/multer/index.js` and just tried to **`console.log('file', file)`** inside the. **`function wrappedFileFilter (req, file, cb) { console.log("file wrappedFileFilter Multer: ", file);.......}`** To my surprise, just adding the `console.log` statement made the upload possible. How can this be, can someone explain this how? Thanks – b Tech Jun 07 '20 at 09:54
  • @HaRdikKaji, did you find the answer for MAC ? I'm getting the same issue. Hope you remember the solution :) – Sergey NN May 04 '22 at 13:33
  • is there any why we can pass error message to the client to tell that the field name you are providing is not same as you specified in upload.single('field name') ? – Kashif Ali Jan 17 '23 at 15:32
  • @SergeyNN Not really :D – HaRdik Kaji May 15 '23 at 08:06
48

A follow up to vincent's answer.

Not a direct answer to the question since the question is using a form.

For me, it wasn't the name of the input tag that was used, but the name when appending the file to the formData.

front end file

   var formData = new FormData();
   formData.append('<NAME>',this.new_attachments)

web service file:

   app.post('/upload', upload.single('<NAME>'),...
Vince Banzon
  • 1,324
  • 13
  • 17
6

This for the Api you could use

 const express        = require('express');
 const bodyParser     = require('body-parser');
 const app = express();
 var multer = require('multer');
 const port = 8000;
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: true }));

 app.listen(port, ()=>{
 console.log('We are live on' + port);
 });

 var upload = multer({dest:'./upload/'});

 app.post('/post', upload.single('file'), function(req, res) {
  console.log(req.file);
 res.send("file saved on server");
 });

This also works fine used on Postman but the file doesn't comes with .jpg extension any Advice? As commented below

This is the default feature of multer if uploads file with no extension, however, provides you the the file object, using which you can update the extension of the file.

var filename = req.file.filename; 
var mimetype = req.file.mimetype; 
mimetype = mimetype.split("/"); 
var filetype = mimetype[1]; 
var old_file = configUploading.settings.rootPathTmp+filename; 
var new_file = configUploading.settings.rootPathTmp+filename+'.'+filetype; 
rname(old_file,new_file);
6

Instead of using the name attribute of the input, use the key you are using to pass the file.

Example: My FormData Object:

Client Side:

formData.append('file', fileBlob);

Server Side:

multer.single('file');
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
hariom sinha
  • 61
  • 1
  • 1
4

since 2 images are getting uploaded! one with file extension and other file without extension. to delete tmp_path (file without extension)

after
src.pipe(dest);

add below code

fs.unlink(tmp_path); //deleting the tmp_path

Kapilrc
  • 1,362
  • 15
  • 11
4

Different file name which posted as "recfile" at <input type="file" name='recfile' placeholder="Select file"/> and received as "file" at upload.single('file')

Solution : make sure both sent and received file are similar upload.single('recfile')

ugali soft
  • 2,719
  • 28
  • 25
3

Unfortunately, the error message doesn't provide clear information about what the real problem is. For that, some debugging is required.

From the stack trace, here's the origin of the error in the multer package:

function wrappedFileFilter (req, file, cb) {
  if ((filesLeft[file.fieldname] || 0) <= 0) {
    return cb(makeError('LIMIT_UNEXPECTED_FILE', file.fieldname))
  }

  filesLeft[file.fieldname] -= 1
  fileFilter(req, file, cb)
}

And the strange (possibly mistaken) translation applied here is the source of the message itself...

'LIMIT_UNEXPECTED_FILE': 'Unexpected field'

filesLeft is an object that contains the name of the field your server is expecting, and file.fieldname contains the name of the field provided by the client. The error is thrown when there is a mismatch between the field name provided by the client and the field name expected by the server.

The solution is to change the name on either the client or the server so that the two agree.

For example, when using fetch on the client...

var theinput = document.getElementById('myfileinput')
var data = new FormData()
data.append('myfile',theinput.files[0])
fetch( "/upload", { method:"POST", body:data } )

And the server would have a route such as the following...

app.post('/upload', multer(multerConfig).single('myfile'),function(req, res){
  res.sendStatus(200)
}

Notice that it is myfile which is the common name (in this example).

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • Thank you so much. Your comment gave me a hint about my error. In my case, I had 2 forms in differents views and differents router files. The first router used the name field with view one and its file name was "imgLoading". The second view had another name the file input. For some reason multer not allows you set differentes name in different views, so I dicided used same name for the file input in both views. – Luis Armando Jul 18 '20 at 01:23
3

2022-2023 Answer

When using FormData and working with arrays, the multer variable name must also include the '[]', this did not use to be the case.

Old code:

const multerConfig = upload.fields([
    {name: 'photos', maxCount: 20}
])

2022+ code:

const multerConfig = upload.fields([
    {name: 'photos[]', maxCount: 20}
])
Leon A
  • 135
  • 1
  • 9
  • 1
    Thank you for your answer! I was using `upload.array()`, and applying this same change resolved my issue. – jaseeey Dec 30 '22 at 00:20
2

I solve this issues looking for the name that I passed on my request

I was sending on body:

{thumbbail: <myimg>}

and I was expect to:

upload.single('thumbnail')

so, I fix the name that a send on request

0

In my scenario this was happening because I renamed a parameter in swagger.yaml but did not reload the docs page.

Hence I was trying the API with an unexpected input parameter.
Long story short, F5 is my friend.

MonoThreaded
  • 11,429
  • 12
  • 71
  • 102
0

probably you are not giving the same name as you mentioned in the upload.single('file') .

Ravi Singh
  • 1,049
  • 1
  • 10
  • 25
0

In my case, I had 2 forms in differents views and differents router files. The first router used the name field with view one and its file name was "inputGroupFile02". The second view had another name for file input. For some reason Multer not allows you set differents name in different views, so I dicided to use same name for the file input in both views.

enter image description here

Luis Armando
  • 101
  • 1
  • 4
0

If you are uploading single and multiple files at the same time it would be recommended not to use middleware as :

app.use(
  multer(
    { 
      storage: fileStorage, 
      limits:
        { 
          fileSize:'2mb' 
        }, 
      fileFilter: fileFilter 
    }
  ).fields(
    [
      { 
        name: 'resume', 
        maxCount: 1 
      }, 
      { 
        name: 'image', 
        maxCount: 1 
      }
    ]
  )
);

Would be suitable for you just to create a simple storage with if function

const storage = multer.diskStorage({
  destination:(req,file,cb)=>{
    if (file.fieldname === 'image'){
      cb(null, './public/images/productcover')
    }
    else if(file.fieldname === 'gallery'){
      cb(null, './public/images/productgallery')
    }
  },
  filename:(req,file,cb)=>{
    cb(null, file.originalname)
  }
})

In my case image is a single file and gallery is a multiple image upload input.

And in the post method to use upload.fields() method

app.post('/products', upload.fields([{
    name: "image"
  }, {
    name: "gallery"
  }]) ,function (req,res){
}
J-J
  • 1
  • 2