I am using express server and multer for upload file on different services (local, azure, cloudinary, amazon s3 etc).
For that i am using different module of multer multer-azure, multer-cloudinary etc.
I need this configuration will be applied to user wise and that information comes from the database.
So i need a extra call to fetch data from database before multer come in action.
I am able to call database query but when i am trying to call multer function, req parameter coming blank. Here is what i am doing.
var multerUtility = require('./upload/multer.utility');
let multer = new multerUtility().getActiveMulterService();
router.post('/', getMetadataConfiguration, multer, (req, res, next) => {
console.log('========== req ==========', req.file); // It is coming blank
console.log('========== req ==========', req.body); // It is coming blank
});
Here is first middleware function, which fetch data from database to verifiy which service will use to upload file.
function getMetadataConfiguration(res, req, next) {
var conn = new jsforce.Connection({
loginUrl : config.org_url,
});
var records = [];
conn.login(username, password, function(err, userInfo) {
if (err) {
return console.error(err);
}
conn.query("query", (err, result) => {
if(err) {
res.status(500).send(err);
}
console.log('=========== result=========', result);
req.serviceConfig = result.records[0];
next();
});
});
}
And here is my MulterUtility Class to handle configuration:
upload/multer.utility.js
class MulterUtility {
constructor() {
}
getActiveMulterService(req, res, next) {
var multerConfiguration;
if(req.serviceConfig.service == 'azure') {
multerConfiguration = multer({
storage: multerAzure({
connectionString: config.azure.connectionString,
account: config.azure.account,
key: config.azure.key,
container: config.azure.container
})
}).single('image');
} else if(req.serviceConfig.service == 'cloudinary') {
multerConfiguration = multer({
storage: cloudinaryStorage({
cloudinary: cloudinary,
folder: config.storageFolder
// allowedFormats: ['jpg', 'png', 'jpeg']
})
}).single('image');
} else if(req.serviceConfig.service === 'amazon') {
multerConfiguration = multer({
storage: multerS3({
s3: s3,
bucket: 'mycontainer',
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname});
},
key: function (req, file, cb) {
cb(null, Date.now().toString() + '-' + file.originalname)
}
})
}).single('image');
} else if(req.serviceConfig.service === 'local') {
multerConfiguration = multer({
storage: multer.memoryStorage()
}).single('image');
}
return multerConfiguration;
}
}
module.exports = MulterUtility;
After executing multer, i am not recieving a req.file or req.body params what multer sets after uploading file.
For now you can consider the 'local' file upload as mentioned in last condition.
The problem is that you call the method getActiveMulterService once before the router. But you need to call it for each post. Try something like this:
var multerUtility = require('./upload/multer.utility');
let multers = new multerUtility();
router.post('/',
getMetadataConfiguration,
(req, res, next) => multers.getActiveMulterService(req, res, next)(req, res, next),
(req, res, next) => {
console.log('========== req ==========', req.file); // It is coming blank
console.log('========== req ==========', req.body); // It is coming blank
});
And in this function you have arguments in the wrong order:
getMetadataConfiguration(res, req, next)
// ==>
getMetadataConfiguration(req, res, next)
Hi this has largely been answered already. The solution is to manual add your file object back onto your req.body object during the process.
Full solution is found here
Related
I have my back-end server in express(node.js) and all apis is running on this server. I also have file-upload mechanism for file-upload api using multer. For file uploading i have created a middleware and in my helper controller i have this
const storage = multer.diskStorage({
destination: (req, file, cb) => {
let path = getFileStoragePath(req, file);
console.log(`path to create ${path}`)
// let path = `uploads/transId${req.body.refrenceId}/transporter`
checkDirectory(path, (err) => {
if (err) {
console.log(`Error occured if checkDirectory ${err.message}`)
cb(err, null)
} else {
cb(null, path);
}
});
},
filename: (req, file, cb) => {
let dateNow = new Date()
cb(null, `${file.fieldname}_${dateformat(dateNow, 'dddd_mmmm_dS_yyyy_h_MM_ss_TT')}${path.extname(file.originalname)}`)
}
});
const saveFilesToFolder = async(req, res, next) => {
const upload = multer({
storage: storage,
fileFilter: imageFilter,
limits: {
fileSize: 1024 * 1024 * 10
}
}).any();
upload(req, res, (err) => {
const wasValidRequest = checkAllowedFiles(req);
if (wasValidRequest.status === false) {
return res.send({
status: false,
message: wasValidRequest.message,
response: null
})
}
// counter = 0
if (err) {
console.log(`Error uploading files, ${err.message}`)
return res.send({
status: false,
message: `Error occurred while uploading files, ${err.message}`,
response: null
})
}
// WHEN FILE UPLOADING IS DONE NOW PASSING THE REQUEST
next();
});
};
And in my route.js file i have attached my middleware to save files into folder and reading files like this
router.post('/upload-files', saveFilesToFolder, catchAsyncErrors(fileController.UploadFiles));
but now my requirement is that i want to read the content of file which is coming in incoming requestwithout saving that file to local disk by accessing the file inside my fileController function and i want to make a separate api for this purpose?
How can i do this
Multer provide memory options by which without storing file in local system, we can convert it into buffer and read the content.
Refer this or this
var storage = multer.memoryStorage();
var upload = multer({ storage: storage });
app.post('/imagenes', upload.single('image_field'), function(req,res){
req.file.buffer;
});
In controller you can use
console.log(String(req.file.buffer))
to look into content
My requirement was to use multer milddleware inside of my controller function to access/read incoming file from request. So i have achieved that and i am giving an answer for future readers so i did like this
controller.js
var storage = multer.memoryStorage({
destination: function(req, file, callback) {
callback(null, '');
}
});
var upload = multer({ storage: storage }).any();
const saveFilesToS3 = async(req, res) => {
upload(req, res, async(err) => {
console.log(req.files[0].buffer) // printing incoming file content as buffer
// rest of the code here
})
}
I'm creating a (POST) route to handle file uploads and also store some other properties to MongoDB in addition to the file path. The problem is when input validation fails, the file is still uploaded in the static(uploads) folder.
I'm using the Multer middleware for file uploads.
Setup
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./uploads/");
},
filename: (req, file, cb) => {
cb(null, Date.now() + "-" + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
cb(null, true);
} else {
//rejects storing a file
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 5
},
fileFilter: fileFilter
});
Fields
const seamUpload = upload.fields([
{ name: "stylePicture", maxCount: 1 },
{ name: "materialPicture", maxCount: 1 }
]);
Route
router.post("/", [auth, seamUpload], async (req, res) => {
const { error } = validateSeam(req.body);
if (error) return res.status(400).send(error.details[0].message);
const seam = new Seam({
stylePicture: req.files["stylePicture"][0].path,
materialPicture: req.files["materialPicture"][0].path,
description: req.body.description,
deliveryDate: req.body.deliveryDate,
customer: req.body.customerId
});
await seam.save();
res.send(seam);
});
Client(PostMan) Screenshot
Me as you faced with this problem.
one solution that I found is when your authentication in all modes seems error, in that case if you have a file from client, you can easily remove it like this:
if(req.file) {
fs.unlink(
path.join(__dirname, "go to root folder that 'req.file.path' leads to the file", req.file.path),
(err) => console.log(err));
}
or then in case that you got multi files, you should do the same way for each of them. If there is, I glad to here that.
I wish there be another way by using multer package for handling that.
good luck
You can have the validation middleware BEFORE the multer middleware.
That way, when the validation fails, all the subsequent middlewares would not be executed -- and thus, the files will not be uploaded.
Separate out the validation like so:
const validateSeamUpload = (req, res, next) => {
const { error } = validateSeam(req.body);
if (error) return res.status(400).send(error.details[0].message);
return next();
};
And then, mount this validateSeamUpload BEFORE the seamUpload middleware like so:
router.post("/", [auth, validateSeamUpload, seamUpload], async (req, res) => {
/** No need for validation here as it was already done in validateSeamUpload */
const seam = new Seam({
stylePicture: req.files["stylePicture"][0].path,
materialPicture: req.files["materialPicture"][0].path,
description: req.body.description,
deliveryDate: req.body.deliveryDate,
customer: req.body.customerId
});
await seam.save();
res.send(seam);
});
By the way, you can pass them as arguments to post() as well. Like so:
router.post("/", /** => See, no need for an array */ auth, validateSeamUpload, seamUpload, async (req, res) => {
/** your controller code */
});
I am giving post request /product/create with some value and an image.
if I console every value before
upload(req, res, (err) => {})
it is showing properly with out image info.
if I receive the value after upload(req, res, (err) => {})
No value is showing.
Full post request code:
app.post('/product/create', (req, res) => {
let filename;
upload(req, res, (err) => {
if(err){
res.render('index', {
msg: err
});
} else {
if(req.file == undefined){
res.render('index', {
msg: 'Error: No File Selected!'
});
} else {
res.render('index', {
msg: 'File Uploaded!',
filename = req.file.filename;
});
}
}
});
const product = {
title : req.body.title,
desc : req.body.desc,
image : filename,
}
});
configuring Multer:
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
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('Error: Images Only!');
}
}
Multer does not support 'req.file.filename' outside upload function. As filename, originalname, fieldname etc is inbuild API of multer. It is limited to upload function only.
Now, if you are trying to upload product values inside database then you have to create an insert function inside multer upload function only.
Don't upload photos to the server, how to solve this problem?
on the page index.ejs a photo gallery should be generated from the added entries. The entry contains a photo. The entry is added, but the photo doesn't load.
project (GitHub)
app/routes.js:
var upload = multer({
storage: storage,
limits: {fileSize: 7},
fileFilter: function (req, file, cd) {
checkFileType(file, cd);
}
}).single('filePhoto');
function checkFiletType(file, cd) {
const fileTypes = /jpeg|jpg/;
const extname = fileTypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = fileTypes.test(file.mimetype);
if (extname && mimetype) {
return cd(null, true);
} else {
cd('Error: only JPEG or JPG!')
}
var Photo = require('../app/models/photo');
module.exports = function (app, passport) {
app.get('/', function (req, res,next) {
Photo.find({}, function (error, photos) {
var photoList = '';
res.render('index.ejs', {photoList: photos});
});
});
}
app.post('/addPhoto', function (req, res, next) {
next();
}, function (req, res) {
var newPhoto = new Photo(req.body);
newPhoto.save().then(function (response) {
console.log('here', response);
res.status(200).json({code: 200, message: 'OK'});
}).catch(function (error) {
console.error('new photo error', error);
});
},function (req, res) {
Photo.find({}, function (error, photos) {
res.send('index.ejs', {
photoList: photos
});
});
});
};
You need to pass your upload var as middleware to your upload route.
Here is a snippet from how I have done it previously:
// Route:
const storage = multer.memoryStorage()
const upload = multer({ storage: storage })
router.post('/upload', upload.single('photo'), ImageController.upload);
// Image Controller:
upload(req, res){
console.log("file", req.file)
}
When I post my image, I make sure I call it photo to match the key word I used in my multer middleware:
So I create my form data like so:
const formData = new FormData()
formData.append('photo', {
uri: data.uri,
type: 'image/jpeg',
});
axios.post(`${SERVER}/images/upload`,
formData: formData,
{ headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => console.log("response", response))
.catch(err => console.log('err', err))
I'm currently implementing a file/image upload service for my users. I want to transform these images (resize/optimize) before uploading to my s3 bucket.
What I'm currently doing: Using a multipart form on my frontend (I think the actual implementation doesn't matter here..) and the multer and multer-s3 packages on my backend.
Here my implementation stripped down to the important parts.
// SETUP
var multer = require('multer');
var s3 = require('multer-s3');
var storage = s3({
dirname: 'user/uploads',
bucket: auth.aws.s3.bucket,
secretAccessKey: auth.aws.s3.secretAccessKey,
accessKeyId: auth.aws.s3.accessKeyId,
region: auth.aws.s3.region,
filename: function (req, file, cb) {
cb(null, Date.now());
}
});
var upload = multer({storage: storage}).single('img');
// ROUTE
module.exports = Router()
.post('/', function (req, res, next) {
upload(req, res, function (err) {
if (err) {
return res.status(401).json({err: '...'});
}
return res.json({err:null,url: '..'});
});
});
What I want to do: transform the image before uploading it. I'm not sure if I need to use multer/busboy here or I can just do it with NodeJS (thus I've tagged NodeJS and express as well).
So my question is: where can I intercept the upload and transform it before uploading it to my S3 bucket?
Not sure if you're still looking for an answer to this, but I had the same problem. I decided to extend the multer-s3 package.
I've opened a pull request to the original repository, but for now, you can use my fork.
Here's an example of how to use the extended version:
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
shouldTransform: function (req, file, cb) {
cb(null, /^image/i.test(file.mimetype))
},
transforms: [{
id: 'original',
key: function (req, file, cb) {
cb(null, 'image-original.jpg')
},
transform: function (req, file, cb) {
cb(null, sharp().jpg())
}
}, {
id: 'thumbnail',
key: function (req, file, cb) {
cb(null, 'image-thumbnail.jpg')
},
transform: function (req, file, cb) {
cb(null, sharp().resize(100, 100).jpg())
}
}]
})
})
EDIT: My fork is also now available via npm under the name multer-s3-transform.
I've tried using #ItsGreg's fork, but couldn't get it to work. I managed to get this behaviour working by using multer-s3 standard configuration, and inside my file upload endpoint, i.e.,
app.post('/files/upload', upload.single('file'), (req, res) => {...})
I am retrieving the file using request, and passing the Buffer to sharp. The following works (and assumes you are using ~/.aws/credentials):
let request = require('request').defaults({ encoding: null });
let dataURI = `https://s3.amazonaws.com/${process.env.AWS_S3_BUCKET}/${image.defaultUrl}`;
request.get(dataURI, function (error, response, body) {
if (! error && response.statusCode === 200) {
let buffer = new Buffer(body);
const sizes = ['thumbnail', 'medium', 'large'];
sizes.forEach(size => {
sharp(buffer)
.resize(image.sizes[size])
.toBuffer()
.then(data => {
// Upload the resized image Buffer to AWS S3.
let params = {
Body: data,
Bucket: process.env.AWS_S3_BUCKET,
Key: `${image.filePath}${image.names[size]}`,
ServerSideEncryption: "AES256",
};
s3.putObject(params, (err, data) => {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
})
})
}
});