38

How can I have multer accept files from multiple file type fields?

I have the following code that uploads a single file, using multer in node.js:

var storage =   multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, './public/uploads');
  },
  filename: function (req, file, callback) {
    callback(null, file.fieldname + '-' + Date.now());
  }
});

var upload = multer({ storage : storage });

app.post('/rest/upload', upload.array('video', 1), function(req, res, next){
    ...
}

From the following form, on the condition only the video field has a value (if I specify both I get an 'Unexpected field' error):

<form action="/rest/upload" method="post" enctype="multipart/form-data">
   <label>Video file: </label> <input type="file" name="video"/> 
   <label>Subtitles file: </label> <input type="file" name="subtitles"/> 
   <input type="submit"/>
</form>

It is not clear from the documentation how to approach this? Any suggestions would be appreciated. BTW I have tried the following parameter variations, without success:

app.post('/rest/upload', [upload.array('video', 1), upload.array('subtitles', 1)] ...
app.post('/rest/upload', upload.array('video', 1), upload.array('subtitles', 1), ...
app.post('/rest/upload', upload.array(['video', 'subtitles'], 1),  ...
Andre M
  • 6,649
  • 7
  • 52
  • 93

7 Answers7

75

What you want is upload.fields():

app.post('/rest/upload',
         upload.fields([{
           name: 'video', maxCount: 1
         }, {
           name: 'subtitles', maxCount: 1
         }]), function(req, res, next){
  // ...
}
mscdex
  • 104,356
  • 15
  • 192
  • 153
21

Using Multer Upload Files From Two Fields of Separate Forms on Different Pages In this example I have two fields - resume and image. Resume in one form and Image in other. Both are on separate pages. First import dependencies

const path = require('path'); // for getting file extension
const multer = require('multer'); // for uploading files
const uuidv4 = require('uuidv4'); // for naming files with random characters

Define fileStorage and fileFilter:

const fileStorage = multer.diskStorage({
  destination: (req, file, cb) => { // setting destination of uploading files        
    if (file.fieldname === "resume") { // if uploading resume
      cb(null, 'resumes');
    } else { // else uploading image
      cb(null, 'images');
    }
  },
  filename: (req, file, cb) => { // naming file
    cb(null, file.fieldname+"-"+uuidv4()+path.extname(file.originalname));
  }
});

const fileFilter = (req, file, cb) => {
  if (file.fieldname === "resume") { // if uploading resume
    if (
      file.mimetype === 'application/pdf' ||
      file.mimetype === 'application/msword' ||
      file.mimetype === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    ) { // check file type to be pdf, doc, or docx
      cb(null, true);
    } else {
      cb(null, false); // else fails
    }
  } else { // else uploading image
    if (
      file.mimetype === 'image/png' ||
      file.mimetype === 'image/jpg' ||
      file.mimetype === 'image/jpeg'
    ) { // check file type to be png, jpeg, or jpg
      cb(null, true);
    } else {
      cb(null, false); // else fails
    }
  }
};

Middleware for multer

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

And then call your routes. You may need to add csrf protection or authentication along with this for security. But this should work fine.

mecaf
  • 75
  • 8
  • 1
    Thank you very much for the clarification i it helped me to understand better. in case of the error ([disallow if statements as the only statement in else blocks (no-lonely-if)](https://eslint.org/docs/rules/no-lonely-if)) you could fix that by using the ternary operator, also with issues at the controller i used `resume: req.files.resume, image: req.files.image` – Emmanuel Neni Apr 22 '20 at 20:44
13

If you want to upload multiple files/images from the same form, I have used the below code and it works fine. The path of the image is stored in the database; I will skip the database path and go straight to the upload function and how the fields are passed to the save function.

    const path = require('path');
    const multer = require('multer');
    const storage = multer.diskStorage({
        destination: (req, file, cb) => {
           if (file.fieldname === "profile") {
               cb(null, './uploads/profiles/')
           }
           else if (file.fieldname === "natid") {
               cb(null, './uploads/ids/');
           }
           else if (file.fieldname === "certificate") {
               cb(null, './uploads/certificates/')
           }
        },
        filename:(req,file,cb)=>{
            if (file.fieldname === "profile") {
                cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
            }
          else if (file.fieldname === "natid") {
            cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
          }
          else if (file.fieldname === "certificate") {
            cb(null, file.fieldname+Date.now()+path.extname(file.originalname));
          }
        }
    });
    const upload = multer({
        storage: storage,
        limits: {
            fileSize: 1024 * 1024 * 10
        },
        fileFilter: (req, file, cb) => {
            checkFileType(file, cb);
        }
    }).fields(
        [
            {
                name:'profile',
                maxCount:1
            },
            {
                name: 'natid', maxCount:1
            },
            {
                name: 'certificate', maxCount:1
            }
        ]
    );
    
    function checkFileType(file, cb) {
        if (file.fieldname === "certificate") {
            if (
                file.mimetype === 'application/pdf' ||
                file.mimetype === 'application/msword' ||
                file.mimetype === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
              ) { // check file type to be pdf, doc, or docx
                  cb(null, true);
              } else {
                  cb(null, false); // else fails
              }
        }
        else if (file.fieldname === "natid" || file.fieldname === "profile") {
            if (
                file.mimetype === 'image/png' ||
                file.mimetype === 'image/jpg' ||
                file.mimetype === 'image/jpeg'||
                fiel.mimetype==='image/gif'
              ) { // check file type to be png, jpeg, or jpg
                cb(null, true);
              } else {
                cb(null, false); // else fails
              }
            }
        }
    //at the save function 

 upload(req, res, (err) => {
        if (err) {
            console.log(err);
        } else {
            if (req.file == "undefined") {
                console.log("No image selected!")
            } else {
                let datecreated = new Date();
                let fullnames = req.body.firstname + ' ' + req.body.lastname;
                let formatedphone = '';
                let phone = req.body.personalphone;
                if (phone.charAt(0) == '0') {
                    formatedphone = '+254' + phone.substring(1);
                } else if ((phone.charAt(0) == '+') && (phone.length > 12 || phone.length <= 15)) {
                    formatedphone = phone
                }
                let teachers = {
                    "teacherid": teacherid,
                    "schoolcode": req.body.schoolcode,
                    "fullnames": fullnames,
                    "email": req.body.email,
                    "dateofbirth": req.body.dateofbirth,
                    "nationalid": req.body.nationalid,
                    "personalphone": formatedphone,
                    "profile": req.files.profile[0].path,
                    "natid": req.files.natid[0].path,
                    "certificate":req.files.certificate[0].path
                }
                connection.query('INSERT INTO teachers SET ?', teachers, (error, results, fields) => {`enter code here`
                    if (error) {
                        res.json({
                            status: false,
                            message: 'there are some error with query'
                        })
                        console.log(error);
                    } else {console.log("Saved successfully");
}
Martin
  • 23
  • 5
Wabz18
  • 138
  • 1
  • 3
  • Can you clarify what's going on at the end of this code block? At minimum, it appears that the indentation is off. But, also, it appears there's a configuration block that is unrelated to the function. I assume that's what your comment is for regarding placing that in the `save()` function? If so, I'd recommend breaking that out into a separate code block. – Jeremy Caney Jun 17 '20 at 19:49
  • Here is the completer save function: below – Wabz18 Jun 19 '20 at 06:42
  • Thank you! You can (and should, in this case) edit your answer to include the `Save()` function as part of the same answer. That way, they don’t become separated over time. Can you do that? – Jeremy Caney Jun 19 '20 at 07:14
  • 1
    Your answer should include an explanation, as mhmdk did in their answer. In its current form it risks getting downvoted. – Andre M Jun 20 '20 at 10:07
6

this worked for Me. complete example

var multer = require('multer')
var storage = multer.diskStorage({


destination: function(req, file, callback) {
    callback(null, './public/audio');
  },
  filename: function(req, file, callback) {
    console.log(file);
    if(file.originalname.length>6)
      callback(null, file.fieldname + '-' + Date.now() + file.originalname.substr(file.originalname.length-6,file.originalname.length));
    else
      callback(null, file.fieldname + '-' + Date.now() + file.originalname);

  }
});

const upload = multer({ storage: storage });


router.post('/save/audio',upload.fields([{
  name: 'audio', maxCount: 1
}, {
  name: 'graphic', maxCount: 1
}]) ,(req, res) => {
  
  const audioFile = req.files.audio[0];
  const audioGraphic = req.files.graphic[0];
  const fileName = req.body.title;


  saveAudio(fileName,audioFile.filename,audioGraphic.filename,req.body.artist,function (error,success) {
    req.flash('success','File Uploaded Successfully')

    res.redirect('/')
  });

})
Zeeshan Mehdi
  • 1,241
  • 1
  • 17
  • 18
4

Did you try to use multer().any()?

Roman Dibikhin
  • 856
  • 4
  • 15
  • I had not, though I'll go with the uploads.fields() approach, as the documentation suggests any() probably is not the best approach, from a handling and security perspective. – Andre M Mar 19 '16 at 01:47
0

I just need upload fields store in a arry

const express = require("express"); 
 const category_route = express(); 
  
 const bodyParser = require('body-parser'); 
 category_route.use(bodyParser.json()); 
 category_route.use(bodyParser.urlencoded({extended:true})); 
 const controller = require('../Controller/Category'); 
 const Multer = require('multer') 
 const Path = require('path'); 
 const multer = require("multer"); 
  
 category_route.use(express.static('public')); 
  const storage = multer.diskStorage({ 
     destination : function(req,files,cb){ 
         cb(null,Path.join(__dirname,'../public/category'),function(err,sucess){ 
             if(err){ 
                 throw err; 
             } 
         }); 
     }, 
     filename:function(req,files,cb){ 
         const name = Date.now()+'-'+ files.originalname; 
         cb(null,name, function(err, sucess){ 
             if(err){ 
                 throw err; 
             } 
         }); 
     }  
  }); 
  
  const upload = multer({storage:storage}) 
  
  
 category_route.post('/add-category',upload.fields([ 
     { 
       name: "icon", 
       maxCount: 1, 
     }, 
     { 
       name: "banner", 
       maxCount: 1, 
     } 
   ]), controller.addCategory); 
  
  
 module.exports = category_route;

/* controller code*/

const Category = require("../Model/Category"); 
  
 const addCategory = async (req, res) => { 
   try { 
  
     var arrIcon = []; 
     for(let i=0; i<req.files.length; i++){ 
       arrIcon[i] = req.files[i].filename; 
     } 
     var arrBanner = []; 
     for(let j=0; j<req.files.length; j++){ 
       arrBanner[j] = req.files[j].filename; 
     } 
  
     const catData = await Category.find(); 
      
     if (catData.length > 0) { 
       let checking = false; 
       catData.every((i) => { 
         if (i.name.toLowerCase() === req.body.name.toLowerCase()) { 
           checking = true; 
           console.log("FOUND"); 
           return false; 
         } 
         console.log("NOT-FOUND"); 
         return true; 
       }); 
       if (checking === false) { 
         const data = new Category({ 
           name: req.body.name, 
           camission: req.body.camission, 
           icon: arrIcon, 
           banner: arrBanner, 
           mtitel: req.body.mtitel, 
           mdiscp: req.body.mdiscp, 
         }); 
         const result = await data.save(); 
         res.send(result); 
       } else { 
         res.send("Category is Already exieet"); 
       } 
     } else { 
       const data = new Category({ 
         name: req.body.name, 
         camission: req.body.camission, 
         icon: arrIcon, 
         banner: arrBanner, 
         mtitel: req.body.mtitel, 
         mdiscp: req.body.mdiscp, 
       }); 
       const result = await data.save(); 
       res.send(result); 
     } 
   } catch (error) { 
     console.log(error); 
     res.send("somthing Wrong"); 
   } 
 }; 
 module.exports = { addCategory };
  • 1
    Your answer could be improved by adding more information on what the code does and how it helps the OP. – Tyler2P Oct 09 '22 at 11:52
-5
upload(req, res, (err) => {
        if (err) {
            console.log(err);
        } else {
            if (req.file == "undefined") {
                console.log("No image selected!")
            } else {
                let datecreated = new Date();
                let fullnames = req.body.firstname + ' ' + req.body.lastname;
                let formatedphone = '';
                let phone = req.body.personalphone;
                if (phone.charAt(0) == '0') {
                    formatedphone = '+254' + phone.substring(1);
                } else if ((phone.charAt(0) == '+') && (phone.length > 12 || phone.length <= 15)) {
                    formatedphone = phone
                }
                let teachers = {
                    "teacherid": teacherid,
                    "schoolcode": req.body.schoolcode,
                    "fullnames": fullnames,
                    "email": req.body.email,
                    "dateofbirth": req.body.dateofbirth,
                    "nationalid": req.body.nationalid,
                    "personalphone": formatedphone,
                    "profile": req.files.profile[0].path,
                    "natid": req.files.natid[0].path,
                    "certificate":req.files.certificate[0].path
                }
                connection.query('INSERT INTO teachers SET ?', teachers, (error, results, fields) => {
                    if (error) {
                        res.json({
                            status: false,
                            message: 'there are some error with query'
                        })
                        console.log(error);
                    } else {

console.log('Saved successfully');}

Wabz18
  • 138
  • 1
  • 3