Multer: Can I set the dest (upload folder) dynamically?

Created on 2 Feb 2018  路  12Comments  路  Source: expressjs/multer

I'd like to upload files to different folder based on user id.

My code is:

const multer = require('multer')
const upload = multer({ dest: 'uploads/'})

app.get('/', function (req, res) {
  res.send('Image Extraction Server');
})

app.post('/upload', upload.array('photos',100), (req,res) => {   
  // get the user id from req
  // how can I change the dest path? 
  res.send('{"status":"ok"}')
})


Is this possible? Thanks.

Most helpful comment

pasting the textual version of the same, so that people can copy-paste easily

const multer = require('multer')
const storage = multer.diskStorage({
destination: (req, file, cb) => {
  const { userId } = req.body
  const dir = ./uploads/${userId}
  fs.exists(dir, exist => {
  if (!exist) {
    return fs.mkdir(dir, error => cb(error, dir))
  }
  return cb(null, dir)
  })
},
filename: (req, file, cb) => {
  const { userId } = req.body
  cb(null, UserId-${userId}-Image-${Date.now()}.png)
}
})

const upload = multer({ storage })

All 12 comments

Indeed you can!

Take a look at DiskStorage in the readme: https://github.com/expressjs/multer#diskstorage

image

pasting the textual version of the same, so that people can copy-paste easily

const multer = require('multer')
const storage = multer.diskStorage({
destination: (req, file, cb) => {
  const { userId } = req.body
  const dir = ./uploads/${userId}
  fs.exists(dir, exist => {
  if (!exist) {
    return fs.mkdir(dir, error => cb(error, dir))
  }
  return cb(null, dir)
  })
},
filename: (req, file, cb) => {
  const { userId } = req.body
  cb(null, UserId-${userId}-Image-${Date.now()}.png)
}
})

const upload = multer({ storage })

@gireeshpunathil ouch sorry.. my bad, you're right :)

Quick note: fs.exists is deprecated.

why when i use req.body its undefined but with params its work??

const storage = multer.diskStorage({
  destination: (req, file, callback) => {
    const flowID = 4;
    const { companyID } = req.body
    const path = `./uploads/${companyID}/${flowID}`;
    fsex.mkdirsSync(path);
    callback(null, path);
  },
  filename: (req, file, cb) => {
    crypto.pseudoRandomBytes(8, (err, raw) => {
      if (err) return cb(err);
      cb(
        null,
        date + "_" + raw.toString("hex") + "_" + path.extname(file.originalname)
      );
    });
  }
});

my Route:

router.post("/uploadfile", upload, (req, res, next) => {
  const file = req.file;
  const{ flowID, companyID} = req.body;
  console.log(companyID);
  if (!file) {
    const error = new Error("Please upload a file");
    error.httpStatusCode = 400;
    return next(error);
  } else {
    createFileInDB(file.originalname, flowID, file.filename)
      .then(() => {
        console.log("File Created");
        res.json(file);
      })
      .catch(err => {
        res.status(500).send(err);
      });
  }
});

@BaruchHeroApps There is a solution by passing the companyID in the path api router:
router.post("/uploadfile/:companyID", upload, (req, res, next) => {
const companyID = req.params.companyID;

And you can also get the companyID in the destination by 'req.params.companyID'

destination: (req, file, callback) => {
const flowID = 4;
const companyID = req.params.companyID;
const path = ./uploads/${companyID}/${flowID};
fsex.mkdirsSync(path);
callback(null, path);
},
........

As fs.exists is deprecated.

Here is my solution:
destination: (req, file, cb) => { const dest = "./app/lab/" + req.userId; fs.access(dest, function (error) { if (error) { console.log("Directory does not exist."); return fs.mkdir(dest, (error) => cb(error, dest)); } else { console.log("Directory exists."); return cb(null, dest); } }); },

@gireeshpunathil , The req.body is undefined. Below is my code.

const multer = require('multer');
const fs = require('fs');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    var dir = './uploads/'+req.params.username;
    try {
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir);
      }
    }catch(err) {
      console.error(err)
    }      
    cb(null, dir);
  },
  filename: function (req, file, cb) {
    var fileArr = file.originalname.split(".");
    fileArr.reverse();
    var fileExt = fileArr[0];
    fileArr.splice(0,1);
     fileArr.reverse();
    var newFileName = fileArr.join(".") + "_" + Date.now() + "." + fileExt;
    newFileName = newFileName.replace(/ /g,"_");
    cb(null, newFileName);
  }
});

const storageMulter = multer({ 
  storage: storage,
  fileFilter: function(req, file, cb) {
    cb(null, true);
  }
});

var uploadConfig = storageMulter.array('files',6);

exports.uploadFiles = function(req,res){
  uploadConfig(req, res, function (err) {
    console.log()
    if (err) {
      console.log(err);
        console.log(req.body);
      return err;
    }else {  
      res.send({files: files});   
    }
  })  
}

As fs.exists is deprecated.

Here is my solution:

destination: (req, file, cb) => {
   const dest = "./app/lab/" + req.userId;
   fs.access(dest, function (error) {
     if (error) {
       console.log("Directory does not exist.");
       return fs.mkdir(dest, (error) => cb(error, dest));
     } else {
       console.log("Directory exists.");
       return cb(null, dest);
     }
   });
 },

creates a corrupted file in my project, I dont know why....

As fs.exists is deprecated.
Here is my solution:

destination: (req, file, cb) => {
   const dest = "./app/lab/" + req.userId;
   fs.access(dest, function (error) {
     if (error) {
       console.log("Directory does not exist.");
       return fs.mkdir(dest, (error) => cb(error, dest));
     } else {
       console.log("Directory exists.");
       return cb(null, dest);
     }
   });
 },

creates a corrupted file in my project, I dont know why....

There might be other reason. I am still using that code.

@rajatmadaan786

Hey, I don't know if you still have this issue, but I had a similar issue. What I found out was that it matters how the fields in req.body are ordered.

In my case, I was trying to allow users to upload images to their own personal folders, i.e. uploads/${userId}. From the front end, I was sending 2 things: userId and image. Since I was sending an image, my content type was multipart/form-data.

When I did:

formData.append('image', image)
formData.append('userId', id)

I wasn't getting access to userId until after calling upload.single('image'), which was the multer middleware function along my /upload route. Apparently, once multer sees the specified field (in my case 'image'), only all the fields until (and including) that field will be accessible to the multer middleware functions. I derived this from the following line in the multer docs on npm:

Note that req.body might not have been fully populated yet. It depends on the order that the client transmits fields and files to the server.

Therefore, I just switched the order of the fields in my formData, making sure 'image' was last, like this:

formData.append('userId', id)
formData.append('image', image)

And I was able to access userId.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

karandutt01 picture karandutt01  路  3Comments

edi picture edi  路  4Comments

nickretallack picture nickretallack  路  4Comments

donjae picture donjae  路  4Comments

Gabxi picture Gabxi  路  3Comments