Multer in Nodejs using Express Router req.file undefined - javascript

I am trying to implement File Upload functionality using multer and Express Router. I defined an endpoint /batch_upload using router.use like below
api.js
router.use(
"/batch_upload",
upload.single("emp_csv_data"),
userController.processBatchUserInformation
);
in userController.js
exports.processBatchUserInformation = async (req, res) => {
console.log(req.file);
if (req.method == "POST") {
try {
console.log("Upload route reached - POST");
console.log(req.file);
console.log(req.file.path);
return res.send(req.file);
} catch (err) {
console.log("Error Occurred");
return res.send(err);
}
}
};
In the FileUploader.js, I defined the upload variable and multer options like below
var multer = require("multer");
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads");
},
filename: (req, file, cb) => {
return cb(null, file.fieldname + "-" + Date.now());
}
});
exports.upload = multer({ storage: storage });
Finally, in the app.js I used the route using
app.use('/user',user_routes)
But when I send a file to http://localhost:5000/user/batch_upload, I get an undefined response for req.file
Irony is that I have the exact implementation in a sample test project and everything seems fine. I don't understand what am I missing. If you see something that seems off, please help me fix it.

So, the reason behind the file not being uploaded was that I did not add Content-type:multipart/form-data in the headers. Thank you guys for trying to help. I appreciate it.

Related

Stop multer from uploading when field is empty

I'm trying to make a post request for users to update multiple parts of their profile at once, but if they only want to update their bio and not their profile picture for example, how can I stop multer from trying to upload if the field is left blank?
post request:
app.post('/updateprofile/:user_id', upload.single("profilePic"), function(req, res){
let newPic = req.file.filename
let bio = req.body.bio
...
})
multer storage options:
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './public/uploads')
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname)
}
})
When I leave it empty the error I get is
TypeError: Cannot read property 'filename' of undefined
just check if any file was added with if (!req.file) { //Dont do anything since there is no file } else { //do something const fileName = req.file.filename; }

How to return an error back to ExpressJS from middleware?

I am using [Multer][1] as middleware to process multipart form data. Multer offers some configuration options for setting destination of file uploads and names called diskStorage. It is within this area that one can do some error checking and control whether Multer authorises a file upload or not.
My Express route is basically this:
expressRouter.post(['/create'],
MulterUpload.single("FileToUpload"), // if this throws an error then have Express return that error to the user
async function(req, res) {
// handle the form text fields in req.body here
});
MulterUpload.single() takes the file input field named "FileToUpload" and sends it off to do this:
const MulterUpload = multer({
storage: MulterStorage
)}
const MulterStorage = multer.diskStorage({
destination: async function (req, file, cb) {
try {
if ("postID" in req.body && req.body.postID != null && req.body.postID.toString().length) {
const Result = await api.verifyPost(req.body.postID)
if (Result[0].postverified == false) {
const Err = new Error("That is not your post!");
Err.code = "ILLEGAL_OPERATION";
Err.status = 403;
throw(Err); // not authorised to upload
} else {
cb(null, '/tmp/my-uploads') // authorised to upload
}
}
} catch (err) {
// How do I return the err back to Express so it can send it to the user? The err is an unresolved Promise as I am using async/await
}
}
,
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
I just can't seem to work out how to get the error from MulterStorage back to Express so that it is sent back the browser/user as an error.
[1]: https://www.npmjs.com/package/multer
You can call the completion callback with an Error object as the first argument. So, instead of
cb(null, someResult)
you call the callback with an error object
cb(new Error("I got a disk error"));
Then, if you have multer set up as plain middleware, this will result in next(err) being called and in Express, your generic error handler will receive the error.
Here are a couple examples:
https://www.npmjs.com/package/multer#error-handling
https://github.com/expressjs/multer/issues/336#issuecomment-242906859

Sending file through HTTP request

I tried to receive the file and store it in the multer storage
Node js code
enter code here
app.post('/createLicence', upload.single('photo'),function(req, res ,next) {
// any logic goes here
console.log("filename" ,req.body.name)
if (!req.file) {
console.log("No file received");
return res.send({
success: false
});
} else {
console.log('file received');
var function_name = 'createLicence'
var arguments_array = [req.file.path,'Raghav','Mumbai','Approved']
invoke = require('/Users/sanjeev.natarajan/fabric-samples/fabcar/invoke.js');
invoke.invokechaincode(function_name,arguments_array)
return res.send({
success: true
})
}
});
but i am receiving no file is receivedi have send the request through postman
-
From : https://www.npmjs.com/package/multer
In order to use the multer package, you have first to define a few parameters so that it can work on your fileDirectory.
In your server.js :
let multer = require('multer');
let storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '/path/to/storage/')
},
filename: function(req, file, callback) {
callback(null, file.originalname + '-' + Date.now());
}
});
let upload = multer({
storage: storage
});
Now, configure your route
router.route('/your/payload')
.post(authController.isAuthenticated, upload.any(), albumController.postFile)
Note that upload.any() will allow you to upload multiple different formatted files at once. Feel free to use any other kind of upload.method() depending on your needs.
From this point, multer already is doing its job, however you might want to keep track of the files uploaded on your server.
So, in your own module, the logic is pretty much straight forward :
(I'm assuming that you're using mongoose models since you're not giving much information, but that's not the relevant part anyway)
exports.postFile = async (req, res) => {
if (!req || !req.files || !req.files[0]) return res.status(400).send("Bad request.");
for (let i = 0; req.files[i]; i++) {
await File.create({
path: req.files[i],
originalName: req.files[i].originalName,
mimetype: req.files[i].mimetype,
owner: req.user.userId
}, (err, file) => {
if (err) console.log("Something went wrong: " + err); else {
// Do something with file
}
});
}
return res.status(418).send("I'm a teapot.");
}
This configuration and middleware use is ONLY for testing purpose, never ever let anyone upload something to your server without carefully handle that uploading process (file integrity, resource management, ...). An open uploading system can become a very wide backdoor getting straight to your server.
Hope this helps,
regards.

nodejs multer diskstorage to delete file after saving to disk

I am using multer diskstorage to save a file to disk.
I first save it to the disk and do some operations with the file and then i upload it to remote bucket using another function and lib.
Once the upload is finished, i would like to delete it from the disk.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage }).single('file')
and here is how i use it:
app.post('/api/photo', function (req, res) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
});
how can i use the diskStorage remove function to remove the files in the temp folder?
https://github.com/expressjs/multer/blob/master/storage/disk.js#L54
update:
I have decided to make it modular and put it in another file:
const fileUpload = function(req, res, cb) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
}
module.exports = { fileUpload };
You don't need to use multer to delete the file and besides _removeFile is a private function that you should not use.
You'd delete the file as you normally would via fs.unlink. So wherever you have access to req.file, you can do the following:
const fs = require('fs')
const { promisify } = require('util')
const unlinkAsync = promisify(fs.unlink)
// ...
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename(req, file, cb) {
cb(null, `${file.fieldname}-${Date.now()}`)
}
})
const upload = multer({ storage: storage }).single('file')
app.post('/api/photo', upload, async (req, res) =>{
// You aren't doing anything with data so no need for the return value
await uploadToRemoteBucket(req.file.path)
// Delete the file like normal
await unlinkAsync(req.file.path)
res.end("UPLOAD COMPLETED!")
})
Multer isn't needed. Just use this code.
const fs = require('fs')
const path = './file.txt'
fs.unlink(path, (err) => {
if (err) {
console.error(err)
return
}
//file removed
})
You may also consider using MemoryStorage for this purpose, with this storage the file is never stored in the disk but in memory and is deleted from the memory automatically after execution comes out of controller block, i.e., after you serve the response in most of the cases.
When you will use this storage option, you won't get the fields file.destination, file.path and file.filename, instead you will get a field file.buffer which as name suggests is a buffer, you can convert this buffer to desired format to do operations on and then upload using a stream object.
Most of the popular libraries support streams so you should be able to use stream to upload your file directly, code for converting buffer to stream:
const Readable = require('stream').Readable;
var stream = new Readable();
stream._read = () => { }
stream.push(file.buffer);
stream.push(null);
// now you can pass this stream object to your upload function
This approach would be more efficient as files will be stored in memory which will result in faster access, but it does have a con as mentioned in multer documentation:
WARNING: Uploading very large files, or relatively small files in
large numbers very quickly, can cause your application to run out of
memory when memory storage is used.
To do it truly automatically across all routes I used this strategy :
when the request ends, we delete all the uploaded files (req.files). Before that, if you want to keep the files on the server, you need to save them in another path.
var express = require('express');
var app = express();
var http = require('http');
var server = http.Server(app);
// classic multer instantiation
var multer = require('multer');
var upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, `${__dirname}/web/uploads/tmp/`);
},
filename: function (req, file, cb) {
cb(null, uniqid() + path.extname(file.originalname));
},
}),
});
app.use(upload.any());
// automatically deletes uploaded files when express finishes the request
app.use(function(req, res, next) {
var writeHead = res.writeHead;
var writeHeadbound = writeHead.bind(res);
res.writeHead = function (statusCode, statusMessage, headers) {
if (req.files) {
for (var file of req.files) {
fs.unlink(file.path, function (err) {
if (err) console.error(err);
});
}
}
writeHeadbound(statusCode, statusMessage, headers);
};
next();
});
// route to upload a file
router.post('/profile/edit', access.isLogged(), async function (req, res, next) {
try {
// we copy uploaded files to a custom folder or the middleware will delete them
for (let file of req.files)
if (file.fieldname == 'picture')
await fs.promises.copy(file.path, `${__dirname}/../uploads/user/photo.jpg`);
} catch (err) {
next(err);
}
});
I have removed directory after file uploaded using fs-extra
const fs = require('fs-extra');
// after you uploaded to bucket
await fs.remove('uploads/abc.png'); // remove upload dir when uploaded bucket

multer file-upload doesn't work with node-windows

I am trying to upload picture files, using multer.
This is is my code:
router.post('/', function (req, res) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
conole.log(err);
return;
}
// Everything went fine
var data = Object.assign({picture: req.file && req.file.filename}, req.body);
db.create('devices', data)
.then(function(data){
res.render('msg', {msg: 'Added device '+ data[0]});
})
.catch(function(err){
console.log(err);
});
});
});
However, when I try to run the server from the command-line node app.js, everything is working as expected. But when I'm running it from A windows service, using node-windows, it doesn't seem to work, while I don't get any errors. (Meaning the picture file name is actually recorded in the database, but the file doesn't upload.
is it maybe a destination issue? if so, I will provide my destination code:
var storage = multer.diskStorage({
destination: './public/uploads/',
filename: function (req, file, cb) {
cb(null, file.originalname + "-" + Date.now() + path.extname(file.originalname));
}
});
remove this line from your code
destination: './public/uploads/',
and add this code
destination: __dirname+'../../../uploads',
this will work both mac OS and windows

Categories