How could I use multer to validate the extension so it can only allow to upload PDF files?
It would be nice to have a filter by extension. multer currently does not have an option property for this.
But you should be able to customize the 'options.onFileUploadStart' function. Pass in something like::
onFileUploadStart: function(file, req, res) {
return (file.ext.toLowerCase() === 'pdf')
}
Did that help?
This is now possible in 1.0.0.
If you want to abort the upload:
multer({
fileFilter: function (req, file, cb) {
if (path.extension(file.originalname) !== '.pdf') {
return cb(new Error('Only pdfs are allowed'))
}
cb(null, true)
}
})
If you want to skip any files that is not pdf:
multer({
fileFilter: function (req, file, cb) {
if (path.extension(file.originalname) !== '.pdf') {
return cb(null, false)
}
cb(null, true)
}
})
EDIT: Nevermind, I did some googling, and see that you are considering a magic numbers implementation.
Is this robust enough to prevent abuse of file upload? Or could someone theoretically upload a malicious file that just has the extension .pdf? I have read a lot about the magic bytes method for validating file-types, but would prefer this if equivalent.
Thanks!
A file (and therefore it's file extension) can be renamed. It's better to check files by mimetype and magic numbers as well. Not sure how to test magic numbers with Multer, but did ask for a field named "magicNumber" https://github.com/expressjs/multer/issues/155.
const path = require('path');
multer({
fileFilter: function (req, file, cb) {
var filetypes = /jpeg|jpg/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Error: File upload only supports the following filetypes - " + filetypes);
}
});
This worked for me:
fileFilter: function (req, file, cb) {
if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
return cb(new Error('Only image files are allowed!'));
}
cb(null, true);
}
To be sure about files types, we need to use magic number.
import fileType from 'file-type';
multer({
fileFilter (req, file, cb) {
// How can I get the buffer to read and test ?
const infoFile = fileType(file.buffer);
}
});
@EmixMaxime
// How can I get the buffer to read and test ?
If you are using memory store, then file.buffer should be set. Otherwise, you can use fs.readFile() to actually read the file from file.path. However, I don't see any reason to use filesystem storage to later read the file back into memory again.
@slavafomin
Where do you see file.path? I mean inside fileFilter.
@jonataswalker I'm not using fileFilter, but if the same instance of the file is passed to it, then you will be able to access file.buffer of file.path depending on the store you are using.
Sorry but this is kind of a newbie question and a bit off topic but how do I handle the error that is thrown? i.e. how do i return this error to the client/user from the server side? Current this results in an internal server error kind of response.
P.S. : I came across this : https://github.com/koajs/koa/wiki/Error-Handling
But again, how do I prevent it from returning any other kind of errors thrown by other things? by something like a SQL lib and only display my own custom errors that I want to be shown?
P.S. 2 : This works, but is it safe? I don't want to return any other error (SQL etc) that would disclose too much information.
async function handleFileError(ctx, next) {
try {
await next()
if (ctx.status === 404) ctx.throw(404)
} catch (err) {
console.error(err.message)
ctx.status = err.status || 500
ctx.body = err.message;
}
}
@pratham2003 The general principle is that you have a central error handler, which captures all the errors of your application and translates them into the HTTP response. I believe all servers should support this one way or another. I know for sure it works in Express and Restify this way.
Then, in your error handler, you can decide what errors and what details should be displayed. It's very convenient to rely on environment here, because you can show all errors with stack traces in development/test and only show error messages in production. Also, you can filter by error type, just make sure to have different classes for different errors.
Also, in the future I would recommend you to ask such questions on StackOverflow, rather than writing an off-topic questions on GitHub.
I am using this:
fileFilter: function(req, file, cb) {
if (file.mimetype !== 'image/png' || file.mimetype !== 'image/gif' || file.mimetype !== 'image/jpeg')
{
return cb(null, false);
} else {
cb(null, true);
}
}
I am using this:
fileFilter: function(req, file, cb) { if (file.mimetype !== 'image/png' || file.mimetype !== 'image/gif' || file.mimetype !== 'image/jpeg') { return cb(null, false); } else { cb(null, true); } }
This should be (notice || replaced with &&):
fileFilter: function(req, file, cb) {
if (file.mimetype !== 'image/png' && file.mimetype !== 'image/gif' && file.mimetype !== 'image/jpeg')
{
return cb(null, false);
} else {
cb(null, true);
}
}
or else it will always fail validation if not a png.
Multer doesn't have File validation using Magic number.
What you could do instead is use NPM packages like read-chunk and file-type to detect the file type after it's uploaded. Since we are using readchunk to read only the beginning of the file as a Buffer, it will not utilize the memory much.
The following are the rules in this approach.
First, get the stored file Route.
Define the variable in advance like
let storedFileRoute = "";
and store the file name to this variable when you process the file using Multer like
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads/");
},
filename: (req, file, cb) => {
ModifiedFileName = Date.now()+"-"+file.originalname.trim().toLowerCase().split(" ").join("-");
storedFileRoute = "uploads/"+ModifiedFileName;
cb(null, ModifiedFileName);
},
});
So, We now have the saved file route. Let's read a portion of that as Buffer using a custom function and readChunk.
function isFileValid(){
readChunk(storedFileRoute, 0, 30).then((value,error)=>{
if(!value || error) return res.sendStatus(500);
fileType.fromBuffer(value).then((value,error)=>{
if(!value || error) return res.sendStatus(500);
console.log(value) // This is the MimeType from Magic Number.
})
});
}
Now, check value against a list of mime-types that you want to support and keep or delete or move the file.
To make this function run, Add this as middleware in express after Multer.
app.use("/upload", upload.single("file"), isFileValid, (req,res,next)=> {
//Then your express stuff here...
})
Wrote the entire answer by hand, sorry if there are any syntax errors. Feel free to ask questions.
This is now possible in
1.0.0.If you want to abort the upload:
multer({ fileFilter: function (req, file, cb) { if (path.extension(file.originalname) !== '.pdf') { return cb(new Error('Only pdfs are allowed')) } cb(null, true) } })If you want to skip any files that is not pdf:
multer({ fileFilter: function (req, file, cb) { if (path.extension(file.originalname) !== '.pdf') { return cb(null, false) } cb(null, true) } })
Just for learning purposes.. where that path attribute is coming from ?
Most helpful comment
A file (and therefore it's file extension) can be renamed. It's better to check files by mimetype and magic numbers as well. Not sure how to test magic numbers with Multer, but did ask for a field named "magicNumber" https://github.com/expressjs/multer/issues/155.