I am trying to use multer for the first time to upload images from node.js to mongodb and I was running a test code as shown below, to see if everything works, i followed the documentation and i can't seem to figure out what the issue is.
the full error is this :
TypeError: that.getDestination is not a function at DiskStorage._handleFile
const router = require("express").Router();
const multer = require("multer");
const storage = multer.diskStorage({
destination: {
function(req, file, callback) {
callback(null, "./uploads/");
},
},
filename: {
function(req, file, callback) {
callback(null, new Date.now + file.originalname);
},
},
});
const upload = multer({ storage:storage });
router.post("/images", upload.single("upload"), (req, res) => {
res.send(req.file);
});
module.exports = router;
multer({ storage:storage });
switch this for
multer({ storage });
I was in the same situation as you.
from what I understand, you want to upload a file to place it in ./upload/ with as name ${Date}${Filename}.
with the code i did i got what you wanted i hope this will help you
const router = require('express').Router();
const multer = require('multer');
const path = require('path');
// this create a path like './uploads/'
const uploadsDir = path.resolve(__dirname, 'uploads');
const storage = multer.diskStorage(
{
destination: uploadsDir,
filename: (req, file, cb) => {
cb(null, `${Date.now()}${file.originalname}`);
},
},
);
const upload = multer({ storage });
router.post("/images", upload.single("upload"), (req, res) => {
res.send(req.file);
});
module.exports = router;
if you want more info i found this in a github issue : https://github.com/expressjs/multer/issues/280
const express = require('express');
const app = express()
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination : (req,file,cb)=>{
cb(null,"./Uploads/")
},
filename : (req,file,cb)=>{
const fileExt = path.extname(file.originalname);
// imp file.jpg
const fileName = file.originalname
.replace(fileExt,"")
.toLowerCase()
.split(" ")
.join("-")+"-"+Date.now();
cb(null, fileName + fileExt)
}
})
const multerObj = multer({
storage : storage, // I did (dist : storage) instead of (storage : storage) do this hope you will get your result.
limits : {fileSize : 100000000},
fileFilter : (req,file,cb)=>{
if(file.mimetype === 'image/jpg' ||
file.mimetype === "image/jpeg" ||
file.mimetype === 'immage/png' ||
file.mimetype === 'application/pdf'){
cb(null,true)
}else(
cb(new Error('Only jpg,jpeg,png file allowed.'))
)
}
});
Read the comment with code carefully and do this hope you will get your desire result.
This is my multer code to upload multiple files.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/files/'+ req.user.id)
},
filename: function (req, file, cb) {
x = file.originalname; //+path.extname(file.originalname);
cb(null,x);
}
});
var upload = multer({storage: storage});
This is the post request where files get submitted on click submit.
router.post(upload.array("FileUpload",12), function(req, res, next) {
//Here accessing the body datas.
})
So what I want is that, I want to create a folder first with the name of the ID generated which can be access from the req.body and then upload those files into that folder respectively.
But since I cannot access the body first before upload I am unable to create that respective folder directory. Is there any other way around which I can think of and implement this?
Updated Solution using fs-extra package.
const multer = require('multer');
let fs = require('fs-extra');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
let Id = req.body.id;
fs.mkdirsSync('./public/files/'+ req.user.id + '/' + Id);
cb(null, './public/files/'+ req.user.id + '/' + Id)
},
filename: function (req, file, cb) {
x = file.originalname; //+path.extname(file.originalname);
cb(null,x);
}
});
var upload = multer({storage: storage});
This is the post request where files get submitted on click submit.
router.post(upload.array("FileUpload",12), function(req, res, next) {
//Here accessing the body datas.
})
you have to install first the fs-extra which will create folder
create seprate folder for multer like multerHelper.js
const multer = require('multer');
let fs = require('fs-extra');
let storage = multer.diskStorage({
destination: function (req, file, cb) {
let Id = req.body.id;
let path = `tmp/daily_gasoline_report/${Id}`;
fs.mkdirsSync(path);
cb(null, path);
},
filename: function (req, file, cb) {
// console.log(file);
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, file.fieldname + '-' + Date.now() + "." + extension);
}
})
let upload = multer({ storage: storage });
let createUserImage = upload.array('images', 100);
let multerHelper = {
createUserImage,
}
module.exports = multerHelper;
in your routes import multerhelper file
const multerHelper = require("../helpers/multer_helper");
router.post(multerHelper , function(req, res, next) {
//Here accessing the body datas.
})
I am currently using the following code to upload a file by using multer (is a node. js middleware for handling multipart/form-data), but currently I am uploading files to the uploads folder in the js node project.
var storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, './uploads')
},
filename: function(req, file, callback) {
var id_photo = uuidv1();
callback(null, id_photo + "-" + file.originalname);
}
})
Now I need to upload the file to folder ABC not upload to folder uploads, through the link provided
for example http://nanana.com/abc
ABC is the name of the folder where I want to fill the file
you have to install first the fs-extra which will create folder here i am creating folder on bases of id in params you can do same like this
create seprate folder for multer like multerHelper.js
const multer = require('multer');
let fs = require('fs-extra');
let storage = multer.diskStorage({
destination: function (req, file, cb) {
let Id = req.params.id;
let path = `tmp/daily_gasoline_report/${Id}`;
fs.mkdirsSync(path);
cb(null, path);
},
filename: function (req, file, cb) {
// console.log(file);
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, file.fieldname + '-' + Date.now() + "." + extension);
}
})
let upload = multer({ storage: storage });
let createUserImage = upload.array('images', 100);
let multerHelper = {
createUserImage,
}
module.exports = multerHelper;
in your routes import multerhelper file
const multerHelper = require("../helpers/multer_helper");
router.post(multerHelper , function(req, res, next) {
//Here accessing the body datas.
})
Managed to store my files in a folder but they store without the file extension.
Does any one know how would I store the file with file extension?
I have a workaround for the adding proper extension of files. If you use path node module
var multer = require('multer');
var path = require('path')
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname)) //Appending extension
}
})
var upload = multer({ storage: storage });
From the docs: "Multer will not append any file extension for you, your function should return a filename complete with an file extension."
Here's how you can add the extension:
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + '.jpg') //Appending .jpg
}
})
var upload = multer({ storage: storage });
I would recommend using the mimetype property to determine the extension. For example:
filename: function (req, file, cb) {
console.log(file.mimetype); //Will return something like: image/jpeg
More info: https://github.com/expressjs/multer
I got file the extension from file.mimetype .
I split the mimetype and get the file extension from it
Please try the below function.
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './uploads')
},
filename: function (req, file, cb) {
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, file.fieldname + '-' + Date.now()+ '.' +extension)
}
})
const upload = multer({ storage: storage })
It can be done like this:
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, config.DIR)
},
filename: function (req, file, cb) {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext)
}
});
const upload = multer({
storage: storage
}).any();
import multer from 'multer';
import * as shortid from 'shortid';
import * as mime from 'mime-types';
const storage = multer.diskStorage({
destination: function (req,file,cb) {
cb(null, '/path/to/uploads/');
},
filename: function (req,file,cb) {
/* generates a "unique" name - not collision proof but unique enough for small sized applications */
let id = shortid.generate();
/* need to use the file's mimetype because the file name may not have an extension at all */
let ext = mime.extension(file.mimetype);
cb(null, `${id}.${ext}`);
}
});
EDIT
shortid has been deprecated you should use nanoid.
import multer from 'multer';
import * as nanoid from 'nanoid';
import * as mime from 'mime-types';
const storage = multer.diskStorage({
destination: function (req,file,cb) {
cb(null, '/path/to/uploads/');
},
filename: function (req,file,cb) {
/* generates a "unique" name - not collision proof but unique enough for small sized applications */
let id = nanoid();
/* need to use the file's mimetype because the file name may not have an extension at all */
let ext = mime.extension(file.mimetype);
cb(null, `${id}.${ext}`);
}
});
There may be some issues in the already answered codes.
There may be some cases of files with no extension.
There should not be an upload.any() usage. Its vulnerable to the attackers
The upload function should not be global
.
I have written the below codes for better security.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'temp/')
},
filename: function (req, file, cb) {
let ext = ''; // set default extension (if any)
if (file.originalname.split(".").length>1) // checking if there is an extension or not.
ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext)
}
})
var upload = multer({ storage: storage });
Using it for upload
// using only single file object name (HTML name attribute)
// May use upload.array(["file1","file2"]) for more than one
app.post('/file_upload', upload.single("file"), function (req,res) {
//console.log(req.body, 'Body');
console.log(req.file, 'file');
res.send("cool");
})
I used this little trick to get file extension, and as a workaround to circumvent issues that might occur when someone uploads a file with similar file name twice, or that exists in the server.
const path = require('path');
const crypto = require('crypto');
let upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, path.join(__dirname, '../uploads'))
},
filename: (req, file, cb) => {
// randomBytes function will generate a random name
let customFileName = crypto.randomBytes(18).toString('hex')
// get file extension from original file name
let fileExtension = path.extname(file.originalname).split('.')[1];
cb(null, customFileName + '.' + fileExtension)
}
})
})
const multer = require('multer');
const uuid = require('uuid/v1');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
};
const fileUpload = multer({
limits: 500000,
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/images');
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, uuid() + '.' + ext);
}
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error('Invalid mime type!');
cb(error, isValid);
}
});
module.exports = fileUpload;
The file extension can be dynamic.
here is the solution
const path = require('path'); // path for cut the file extension
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
cb(null, 'upload_at_' + Date.now() + path.extname(file.originalname))
}
})
I use this method and it works.
I store the file in this format:
FieldName+Date+Extension => Profile1621416613594.jpg
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req,file,cb){
cb(null, './uploads')
},
filename: function (req,file,cb){
cb(null,file.fieldname+'-'+Date.now()+'.'+file.mimetype.split('/').reverse()[0]);
},
});
var upload = multer({storage: storage});
I am doing like this
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/img/')
},
filename: function (req, file, cb) {
let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
cb(null, Date.now() + ext);
}
})
var upload = multer({ storage: storage }).single('eventimage');
An object oriented way to store image with unique name
// image.service.ts
import { diskStorage, StorageEngine } from "multer";
class ImageStorageService {
storage: StorageEngine
constructor() {
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpg',
'image/jpg': 'jpg'
}
this.storage = diskStorage({
destination: (req, file, callback) => {
const isValid = MIME_TYPE_MAP[file.mimetype]
let error = new Error(`Invalid mime type`)
if (isValid)
error = null
//app.use(express.static(path.join(`${__dirname}/assets`)))
callback(error, 'assets/images')
},
filename: (req, file, callback) => {
let currentFileName: string = file.originalname.substr(0, file.originalname.lastIndexOf('.'))
const name = currentFileName.toLowerCase().split(' ').join('-')
const ext = MIME_TYPE_MAP[file.mimetype]
callback(null, `${name}-${Date.now()}.${ext}`)
}
})
}
}
export const ImageStorage = new ImageStorageService().storage
then in one of your routes
import { ImageStorage } from "./services/image-storage.service";
this.router.post('/signup', multer({ storage: ImageStorage }).single('image'), async (req, res, next) => {
let img_url: string
if (req.file) {
const url: string = `${req.protocol}:\/\/${req.get('host')}`
img_url = url + '/images/' + req.file.filename
//http://localhost:3000/images/penguins-1548339248380.jpg
}
})
I like to use the original filename for SEO purposes. This requires a bit more checking if the file with the same name already exists.
Moreover, extension resolving is done in a few steps to provide maximum flexibility.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
// try to get extension from original file name
var lioDot = file.originalname.lastIndexOf('.');
if (lioDot !== -1) {
// I like to use original upload filename for SEO but first lets clean it
var newName = file.originalname.substring(0, lioDot).replace(/([^a-z0-9]+)/gi, '-');
var ext = file.originalname.substring(lioDot, file.originalname.length);
} else {
var newName = file.originalname.replace(/([^a-z0-9]+)/gi, '-');
// try to get extension from mime type string
var extArray = file.mimetype.split("/");
var ext = extArray[extArray.length - 1];
// mime type extension resolving by pure string extraction is not accurate for a lot of types
// https://www.freeformatter.com/mime-types-list.html
// it's usually fine for ext strings up to 4 characters, png, jpeg, gif, bmp, tiff ..
if (ext > 4) {
// other mime types you would like to support
var mimetypes = { 'vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx' };
if (mimetypes.hasOwnProperty(ext)) ext = mimetypes[ext];
}
}
var newFullName = newName + ext;
var i = 0;
// we need to check if the file with the same name already exists
// if it exists then we're adding something to make it unique
while (fs.existsSync(process.env.PWD + '/uploads/' + newFullName)) {
newFullName = newName + '-' + ++i + ext;
}
cb(null, newFullName);
}
})
const upload = multer({ storage: storage });
It can be done like this...simple to grasp
// validate uploaded files
const FILE_TYPE_MAP = {
// mime type
"image/png": "png",
"image/jpeg": "jpeg",
"image/jpg": "jpg",
};
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "public/uploads");
},
filename: function (req, file, cb) {
const filename = file.originalname.replace(" ", "-");
const extension = FILE_TYPE_MAP[file.mimetype]
cb(null, `${filename}-${Date.now()}.${extension}`);
},
});
Simple helpler function that maintains the unique filename generated by multer and adds the extension parsed from mimetype:
Just pass the object returned by multer
const fs = require('fs');
function renameWithExt(file) {
const ext = file.mimetype.split('/')[1]; // parse the extension type
fs.rename(`${file.path}`, `${file.path}.${ext}`, () => {
console.log(`File: ${file.filename} renamed with extension '.${ext}'`);
});
}
renameWithExt(req.file);
const multer = require('multer');
const uuid = require('uuid/v1');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
};
const fileUpload = multer({
limits: 500000,
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/images');
},
filename: (req, file, cb) => {
const ext = MIME_TYPE_MAP[file.mimetype];
cb(null, uuid() + '.' + ext);
}
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let error = isValid ? null : new Error('Invalid mime type!');
cb(error, isValid);
}
});
module.exports = fileUpload;