I am using multer-s3 to upload files to was S3. When i upload a file it all works fine and I have never had an error, however most tutorials do not mention anything about how to handle a case where there may be an issue with uploading files.
My upload is as follows
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'my bucket',
key: function (req, file, cb) {
console.log(file);
cb(null, file.originalname);
}
})
});
And i call it from the
app.post('/File/Add' , storage_functions.upload.single('file'), function (req, res) {
console.log('file uploaded')
});
does multers3 provide some kind of response on upload. This would be useful to access errors but would also be useful if the response contained the file path in s3.
You can add callback function to handle errors.
const upload = storage_functions.upload.single('file');
app.post('/File/Add', function (req, res) {
upload(req, res, function (err) {
if (err) {
// your error handling goes here
}
});
});
To find more details, multer documentation
This is what i did for handling errors:
I have created a file s3.js, which includes multer-s3 configuration and error handler
import S3 from 'aws-sdk/clients/s3.js'
import multer from 'multer'
import multerS3 from 'multer-s3'
import dotenv from 'dotenv'
dotenv.config()
const region = process.env.S3_REGION
const s3 = new S3({
region
})
// upload to S3 storage
export const uploadFiles = (req, res, next) => {
const upload = multer({
limits: { files: 6 },
storage: multerS3({
s3: s3,
bucket: process.env.S3_BUCKET_NAME,
metadata: function(req, file, cb) {
cb(null, { fieldName: file.fieldname })
},
key: function(req, file, cb) {
cb(null, `images/users/${ req.userId }/products/${ req.body.name }/${ new Date().toISOString() }-${ file.originalname }`)
},
})
}).array('photos', 6)
// Custom error handling for multer
upload(req, res, (error) => {
if (error instanceof multer.MulterError)
return res.status(400).json({
message: 'Upload unsuccessful',
errorMessage: error.message,
errorCode: error.code
})
if (error)
return res.status(500).json({
message: 'Error occured',
errorMessage: error.message
})
console.log('Upload successful.')
next()
})
}
You can now use this on the route that you want to upload files to.
import express from 'express'
import { uploadFiles } from '../config/s3.js'
const router = express.Router()
router.post('/upload', uploadFiles, async(req, res) => {
console.log(req.files)
})
export default router
Because I set the file limit to 6 I will get the following response when I try to send more than 6 files.
Related
I am trying to upload images in an express server using multer, however, uploading images using postman using the route below, gives the json message { msg: 'image uploaded successfully' } (i.e., the route is reached correctly), but req.file gives undefined. Why? the related file structure is as follows, to make sure I am referencing the destination correctly:
-backend
--routes
---uploadRoutes.js
--server.js
-frontend
-uploads
uploadRoutes.js
import path from 'path';
import express from 'express';
import multer from 'multer';
const router = express.Router();
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, '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) => {
console.log(req.file);
try {
res.status(200).json({ msg: 'image uploaded successfully' });
} catch (error) {
console.error(error.message);
}
// res.send(`/${req.file.path}`);
});
export default router;
just check the header and body form-data request, because your code is correctly if you have this line in the app file
app.use("/uploads", express.static("uploads"));
header of request
This is my first time ever using multer or s3, I followed along with a blog and I seem to be running into some issues. In my routes.js when I return res.json({'imageUrl': req.file.location}) I get this error: Cannot read property 'location' of undefined
and when I console.log() req.file it returns undefined.
I'm confident that my bucket and all that is configured correctly, I'm just a bit confused as to what's going on in the code. I would really appreciate if someone could point out what I'm doing wrong or has any suggestions
Thanks!
update: I can successfully upload images through postman, but not the form in my project. I'm pretty sure my problem is coming from return res.json({'imageUrl': req.file.location})
// uploadController.js
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
require("dotenv").config();
aws.config.update({
secretAccessKey: process.env.SECRETACCESSKEY,
accessKeyId: process.env.ACCESSKEYID,
region: "us-west-2",
});
const s3 = new aws.S3();
const fileFilter = (req, file, cb) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
cb(new Error("Invalid mime type, only JPEG and PNG"), false);
}
};
const upload = multer({
fileFilter: fileFilter,
storage: multerS3({
s3: s3,
bucket: "mylocal",
acl: "public-read",
metadata: function (req, file, cb) {
cb(null, { fieldName: "TESTING_META_DATA" });
},
key: function (req, file, cb) {
cb(null, Date.now().toString());
},
}),
});
module.exports = upload;
// routes.js
let upload = require('../controllers/uploadController')
router.get("/upload", function (req, res) {
res.render("upload");
});
const singleUpload = upload.single("image");
router.post("/upload", function (req, res) {
singleUpload(req, res, function (err) {
if (err) {
return res.status(422).send({ errors: "wrong" });
}
console.log(req.file); // returns undefined
return res.json({'imageUrl': req.file.location})
});
});
// upload.ejs
<form method="POST" enctype="multipart/form-data">
<input type="file" />
<button type="submit">upload</button>
</form>
Try req.files(please note s at the end) Instead of req.file
I am using multer and it works fine in the http object, here is the code:
server = http.createServer(function(req, res){
var upload = multer({ storage : multerhelper.storage}).single('userFile');
upload(req, res, function(err) {
if(err)
console.log("Error uploading the file");
});
});
The moment I take this piece of code outside of the http object inside another file, it doesn't work anymore.
handlers._users.post = function(req, res, data, callback){
var upload = multer({ storage : multerhelper.storage}).single('userFile');
upload(req, res, function(err) {
if(err)
console.log("Error uploading the file");
callback(400, {'Message' : 'Done'});
});
});
Your help is appreciated.
You can make a file which will have the multer logic and export this file so that you can get the multer functionality.
example:
const multer = require("multer");
// store the file reference through multer
const storage = multer.diskStorage({
destination: function (req, file, callback) {
//Where file should be store
callback(null, __base + "/public/uploads");
},
filename: function (req, file, callback) {
callback(null, file.originalname);
}
});
//make sure file is image as the jpeg or png
const fileFilter = (req, file, callback) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png" || file.mimetype === 'application/octet-stream') {
callback(null, true);
}
callback(null, false);
};
export const upload = multer({
storage,
limits: {
fileSize: 1024 * 1024 * 5000
},
fileFilter
});
Now import this file as
import {
upload
} from "/filepath";
I am trying to upload an image using express but I am facing two problems, first, whenever I upload the same image again it's not getting uploaded and secondly after uploading any single image a file with image also uploading. Here is my code.
var multer = require('multer');
var uploads = multer({dest: './images'});
app.post('/uploading', uploads.single("file"), function (req, res) {
var file = __dirname +"/images" + "/" + req.file.originalname;
fs.readFile( req.file.path, function (err, data) {
fs.writeFile(file, data, function (err,data) {
if( err ){
console.error( err );
response = {
message: 'Sorry, file couldn\'t be uploaded.',
filename: req.file.originalname
};
}else{
response = {
message: 'File uploaded successfully',
filename: req.file.originalname
};
}
res.end( JSON.stringify( response ) );
});
});
})
The uploads.single("file") middleware Will handle the file upload. You don't have to specifically fs.read and fs.write the file.
var multer = require('multer');
var uploads = multer({dest: './images'});
app.post('/uploading', uploads.single("file"), function (req, res) {
//the file is uploaded automatically
})
EDIT: The above code will upload the file with hex string as filename without any extension.
In order to add rename function you need to use diskStorage. Here is the code taken from this github issue page.
var multer = require('multer');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './images/')
},
filename: function (req, file, cb) {
crypto.pseudoRandomBytes(16, function (err, raw) {
cb(null, raw.toString('hex') + Date.now() + '.' + mime.extension(file.mimetype)); //this is the rename func
});
}
});
var uploads = multer({ storage: storage });
app.post('/uploading', uploads.single("file"), function (req, res) {
//the file is uploaded automatically
})
Now you can use the uploads variable as middleware as shown in the above snippet.
you can edit the filename: function (req, file, cb) { .. } according to your needs. Now the filename will be, <16characterhexstring>.ext
another way to handle it will be not using middleware and using multer manually with below options :
try {
var storage = multer.diskStorage({
destination: function(request, file, callback) {
//define folder here by fs.mkdirSync(anyDirName);
},
filename: function(req, file, callback) {
callback(null, anyFileName);
},
limits: self.limits
});
var upload = multer({
storage: storage,
fileFilter: function(request, file, callback) {
// here you can filter out what not to upload..
}
}).any();
upload(request, response, callback);
} catch (e) {
}
hope this helps!
I am using multer to save the file on server developed through express & nodejs.
I am usign following code.
var express = require('express'),
multer = require('multer')
var app = express()
app.get('/', function(req, res){
res.send('hello world');
});
app.post('/upload',[ multer({ dest: './uploads/'}), function(req, res){
res.status(204).end()
}]);
app.listen(3000);
Multer saves the file for me in the specified destination folder.
All this is working fine but I have following questions:
If the file saving fails for various reasons, it looks like my route will always return status 204.
I am not sure if status 204 is retured after file is saved or while the file is getting saved asynchronously, status 204 is returned.
This is how to write multer middleware that handle upload and errors
const multer = require("multer");
function uploadFile(req, res, next) {
const upload = multer().single('yourFileNameHere');
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
next()
})
}
You can handle errors using the onError option:
app.post('/upload',[
multer({
dest : './uploads/',
onError : function(err, next) {
console.log('error', err);
next(err);
}
}),
function(req, res) {
res.status(204).end();
}
]);
If you call next(err), your route handler (generating the 204) will be skipped and the error will be handled by Express.
I think (not 100% sure as it depends on how multer is implemented) that your route handler will be called when the file is saved. You can use onFileUploadComplete to log a message when the upload is done, and compare that to when your route handler is called.
Looking at the code, multer calls the next middleware/route handler when the file has been uploaded completely.
Try this
var upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
return
}
// Everything went fine
})
}
ref :
http://wiki.workassis.com/nodejs-express-get-post-multipart-request-handling-example/
https://www.npmjs.com/package/multer#error-handling
As you can see from the code below (the source from the muter index.js file), if you not pass the onError callback the error will be handled by express.
fileStream.on('error', function(error) {
// trigger "file error" event
if (options.onError) { options.onError(error, next); }
else next(error);
});
Be careful with the system when the user sends anything to you
I usually set more [*option1]:
process.on('uncaughtException', function(ls){
// console.log(ls);
(function(){})();
});
And then:
var upload= multer({ dest: __dirname + '/../uploads/' }).single('photos');
// middle ..
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
console.log('MulterError', err);
} else if (err) {
// An unknown error occurred when uploading.
// Work best when have [*option1]
console.log('UnhandledError', err);
}
if(err) {
return res.sendStatus(403);
}
res.sendStatus(200);
});
package: "multer": "^1.4.2"
var multer = require('multer')
var upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
handle error
} else if (err) {
handle error
}
else{
write you code
}
})
})
you can see this from the documentation
According to the multer documentation (https://github.com/expressjs/multer#error-handling)
Error handling
When encountering an error, Multer will delegate the error to Express. You can display a nice error page using the standard express way.
If you want to catch errors specifically from Multer, you can call the middleware function by yourself. Also, if you want to catch only the Multer errors, you can use the MulterError class that is attached to the multer object itself (e.g. err instanceof multer.MulterError).
code sample
const multer = require('multer')
const upload = multer().single('avatar')
app.post('/profile', function (req, res) {
upload(req, res, function (err) {
if (err instanceof multer.MulterError) {
// A Multer error occurred when uploading.
} else if (err) {
// An unknown error occurred when uploading.
}
// Everything went fine.
})
})
When we re-write the code in this question with latest version of multer (v1.4.5-lts.1)
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({ dest: './uploads/' }).single('fieldName');
app.get('/', (req, res) => {
res.send('hello world');
});
app.post(
'/upload',
(req, res, next) => {
upload(req, res, (err) => {
if (err instanceof multer.MulterError) {
res.status(404).send(err + 'Upload failed due to multer error');
} else if (err) {
res.status(404).send(err + 'Upload failed due to unknown error');
}
// Everything went fine.
next();
});
},
(req, res) => {
res.status(204).end();
}
);
app.listen(3000);
To check the Multer errors and non multer errors we can add validations using fileFilter and limits
eg:
I am adding a CSV file filter method and some limits
// CSV file filter - will only accept files with .csv extension
const csvFilter = (req, file, cb) => {
console.log('csv filter working');
if (file.mimetype.includes('csv')) {
cb(null, true);
} else {
cb('Please upload only csv file.', false);
}
};
// adding the csv file checking, file number limit to 1 and file size limit 10 1kb
const upload = multer({
dest: './uploads/',
fileFilter: csvFilter,
limits: { files: 1, fileSize: 1024 }
}).single('fieldName');
We can see different errors are thrown when we try to upload non CSV file or >1kb sized file or multiple files.
I know its late but it might help others.
Here is how I handle errors and use it safely in my express/typescript
project.
const upload = (fieldName: string) => {
return (req: Request, res: Response, next: NextFunction) => {
return multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
if (file.fieldname === 'post') {
return cb(null, `${path.join(path.dirname(__dirname), 'uploads/postImg')}`);
} else if (file.fieldname === 'profile') {
return cb(null, `${path.join(path.dirname(__dirname), 'uploads/ProfilePic')}`);
} else {
return cb(new Error(`${file.fieldname} is incorrect`), null);
}
},
filename: (req, file, cb) => {
return cb(null, `${file.originalname}-${Date.now()}-${file.fieldname}`);
},
}),
fileFilter: (req, file, cb) => {
const fileExtension = file.mimetype.split('/')[1];
if (!(fileExtension in allowedFiles)) return cb(null, false);
return cb(null, true);
},
dest: `${path.join(path.dirname(__dirname), 'uploads')}`,
limits: {
fileSize: 1024 * 1024 * 3, // 3MB
files: 1,
},
}).single(fieldName)(req, res, (err: any) => {
if (err instanceof multer.MulterError) {
// handle file size error
if (err.code === 'LIMIT_FILE_SIZE') return res.status(400).send({ error: err.message });
// handle unexpected file error
if (err.code === 'LIMIT_UNEXPECTED_FILE') return res.status(400).send({ error: err.message });
// handle unexpected field key error
if (err.code === 'LIMIT_FIELD_KEY') return res.status(400).send({ error: err.message });
}
next();
});
};
};
app.post("/upload", (req: Request, res:Response)=>{
res.json({message:"file uploaded"})
})