uploads folder as static using express not working - node.js

NOTE: I have tried and seen other similar issues but none resolved my problem thus posting again, thanks.
Hey everyone! I am trying to access the images uploaded in a folder at the backend/uploads directory which is marked as static but its not working as expected.
uplaodRoutes.js
const path = require("path");
const express = require("express");
const multer = require("multer");
const router = express.Router();
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, "backend/uploads/");
},
filename(req, file, cb) {
cb(
null,
`${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`
);
},
});
function checkFileType(file, cb) {
const filetypes = /jpg|jpeg|png/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (extname && mimetype) {
return cb(null, true);
} else {
cb("Images only!");
}
}
const upload = multer({
storage,
fileFilter: function (req, file, cb) {
checkFileType(file, CB);
},
});
router.post("/", upload.single("image"), (req, res) => {
req.file.size = req.file.size / 1000;
res.send(req.file);
});
module.exports = router;
server.js
app.use(
"/uploads",
express.static(path.join(path.resolve(), "backend/uploads"))
);
as we can see we cannot access it in browser.

const upload = multer({
**storage: fileStorageEngine,** add fileStorageEngine Parameter
storage: fileStorageEngine,
fileFilter: function (req, file, cb) {
**change from CB to cb**
checkFileType(file, cb);
},
});

Related

Uploading file using multer triggers process.on('SIGINT')

When I upload file using multer nodemon restarts web server. It's fine on my local machine which is windows. It only happens when I start my server on ubuntu machine. What's the problem? My code is here below.
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(__dirname, '../public/uploads/'))
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
});
function checkFileType(file, cb) {
const filetypes = /jpeg|jpg|png|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb('Only images! jpeg|jpg|png|gif ');
}
}
const upload = multer({
storage: storage,
limits: { fileSize: 5242880 },
fileFilter: function (_req, file, cb) {
checkFileType(file, cb);
}
});
router.route('/upload').post(protect, upload.array('banner', 12), (req, res, next) => {
console.log(req.files)
if (!req.files) {
res.status(400).json({ success: false, message: 'File not found' });
return;
}
const data = [];
const baseUrl = `THE_URL_FILE_UPLOAD`;
req.files.map((file, idx) => {
data.push({ path: baseUrl + file.originalname })
})
res.status(200).json({ success: true, data })
});

TypeError: that.getDestination is not a function

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.

Issue with multer and router.post(). cannot read property 'any'

I have a multer issue when I am trying to use it my .post path.
My route looks like this:
const fileUpload = require('../middleware/fileUpload');
router.post(
'/projects/create/:userId',
authCheck,
isAdmin,
addProjectToUser,
fileUpload.any(),
create
);
And my file upload is:
const multer = require('multer');
const { v1: uuidv1 } = require('uuid');
const MIME_TYPE_MAP = {
'image/png': 'png',
'image/jpg': 'jpg',
'image/jpeg': 'jpeg',
};
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, uuidv1() + '.' + ext);
},
}),
fileFilter: (req, file, cb) => {
const isValid = !!MIME_TYPE_MAP[file.mimetype];
let err = isValid ? null : new Error('Invalid Mime Type!');
cb(err, isValid);
},
});
module.export = fileUpload;
I do get const photUpload = fileUpload.any(); TypeError: Cannot read property 'any' of undefined
Not sure wht is worng with this. Any suggestions?
module.exports = fileUpload;
Notice the extra s in exports

Multer fileFilter is not getting called and req.body is empty

for some reason the fileFilter on multer is not getting called.
here is my Controller (i am using express routers)
const express = require('express');
const router = express.Router();
const UploadController = require('../controllers/UploadController');
router.route('/upload').post(UploadController.upload);
module.exports = router;
and this is the controller
const multer = require('multer');
const fs = require('fs');
module.exports = {
upload: function (req, res) {
let storage = multer.diskStorage({
destination: function (req, file, cb) {
console.log('here');
const filesDir = './uploads/' + req.body.ref;
if (!fs.existsSync(filesDir)) {
fs.mkdirSync(filesDir);
}
cb(null, filesDir);
},
filename: function (req, file, cb) {
let extArray = file.mimetype.split("/");
let extension = extArray[extArray.length - 1];
cb(null, req.body.type + '-' + Date.now() + '.' + extension);
},
fileFilter : function (req, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
}
})
const upload = multer({ storage: storage }).single('file');
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
res.send({
error: err
});
} else if (err) {
res.send({
error: err
});
}else{
res.send({
file: req.file,
body: req.body
});
}
});
}
}
I have following issues:
The fileFilter function is not even called so its not validating files
the req.body on the upload function (upload: function (req, res)) is empty it is only available in diskStorage and last upload function (upload(req, res, function (err)) so i cannot validate the body data also.
I had the same problem. the fileFilter function has to be defined inside multer, not inside the diskStorage function.
To make it a bit more readable I defined the storage and filter in variables instead of making everything inside the multer call.
// storage settings
const multerStorage = multer.diskStorage({
destination: function(req, file, next) {
next(null, './public/files');
},
filename: function(req, file, next) {
const sanitizedName = file.originalname
.replace('/[^a-z0-9\./gi', '-')
.replace('/-{2,}/g', '-')
.toLowerCase();
const name = Date.now() + '-' + sanitizedName;
// sending the file name to be stored in the database
req.body.filename = name;
next(null, name);
},
limits: {
fileSize: 25000000
}
});
// filter function
const multerFilter = function(req, file, cb) {
const ext = path.extname(file.originalname).toLowerCase();
if (ext !== '.pdf') {
cb(new Error('File must be in PDF format.'));
}
cb(null, true);
}
And then applying the storage settings and filter function to multer:
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter
});

How to store a file with file extension with multer?

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;

Resources