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; }
Related
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.
I am creating API using express JS. Now, I have a router which will be used to upload image using multer.
Here is my router :
const multer = require('multer');
module.exports = (app) => {
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null , file.originalname);
}
});
const upload = multer({ storage: storage });
// I have also tried this but not working
// const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('image'), (req, res, next) => {
res.status(201).json({
message: "File uploaded successfully"
});
});
}
Now, from my reactjs app I am calling this router using axios like this :
const headers = {
"Content-Type": "multipart/form-data"
}
const body = new FormData();
body.append('image', this.state.selectedCategoryImage);
axios.post('http://localhost:3000/upload', body, { headers }).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
In above code this.state.selectedCategoryImage is a selected image from html <input> tag.
Now, When I call this api I am getting my response "file uploaded successfully", but I am not able to see my uploaded image anywhere in public directory. My image is not uploading.
Please anyone can help me what's the issue ?
Pass file Object not URL
URL.createObjectURL(file) // it return file url that you can use to show file preview
For upload file, send actual file in axios API as you got from file input
var file = event.target.files[0]; // return actual file
this way it actually send file object.
I know that I can change the filename with multer by means of the storage object like following:
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, process.env.UPLOAD_DIR);
},
filename: (req, file, cb) => {
cb(null, 'bla.png');
}
});
const upload = multer({ storage: storage } );
My request, besides having the file, also contains some text attributes such as name: myPic.png.
Is it possible to dynamically change the filename dependent on other request attributes or within the controller like following:
filename: (req, file, cb) => {
cb(null, `${req.body.name}.png`);
}
or
router.post('/upload', upload.single('pic'), myController.upload);
/* in controller */
upload = async (req: Request, res: Response) => {
try {
/* change the filename of multer here? */
} catch (err) {
winston.error(`Error while uploading: ${err.message}`);
winston.error(`Stack trace: ${err.stack}`);
sendJSONResponse(res, err, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Multer is the middleware which both populates req.body AND stores the file.
Also, when it reaches the filename() function, there is no guarantee that the text fields will be populated in req.body because it depends on which order the client sends them in (see last note).
From what I see, you have two options:
1) Rename the uploaded file after the multer upload middleware does its thing and populates req.body as well as req.file. So in your controller upload middleware, you'd do something like:
if (req.file) {
fs.renameSync(req.file.path, req.file.destination + req.body.name);
}
2) Change the request body text field into a query parameter. Then, inside filename() you can do a req.query.name.
Con: Not a very RESTful design, but maybe that is not so important to you.
According to the multer documentation it does not have access to req.body for other additional fields, if you test it it receives an undefined value, then a not so perfect but functional solution is the following, once the file is uploaded you can rename it as follows.
Add the native class fs for access to files option
const fs = require('fs');
In diskStorage configuration add the name you want, for example bla.png
var storage = multer.diskStorage({
destination: path.join('public/images/'),
filename: function ( req, file, cb ) {
cb(null, 'bla.png');
}
});
The form with the text field for the custom name
<form action="/upload" enctype="multipart/form-data" method="POST">
<input type="file" accept="image/*" name="photo" >
<br><!--here is the custom file name-->
<input type="text" name="file_name">
<br>
<button type="submit">Send</button>
</form>
Within the post path, once you have sent the file whose name will be bla.png, you can replace that name with the one in a field of the form by accessing req.body.field_name
router.post('/upload', upload.single('photo'), (req, res) => {
//Here change the file name bla.png for the new value in req.body.field_name + original ext of file
fs.renameSync(req.file.path, req.file.path.replace('bla.png',
req.body.field_name + path.extname(req.file.originalname)));
if(req.file) {
res.json(req.file);
}
else throw 'error';
});
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.
I want to create a link (./pictures) of a picture which is already uploaded and add the link to my MySQL-DB.
Picture Save:
var Storage = multer.diskStorage({
destination: function(req, file, callback) {
callback(null, "./pictures");
},
filename: function(req, file, callback) {
pictureSaveFormat = Date.now();
file.originalname);
callback(null, pictureSaveFormat + ".jpg");
}
});
This only pseudo/example code but I hope it helps:
const storage = multer.diskStorage({
destination(req, file, cb) { /* save to destination */},
filename(req, file, cb) { /* make filename */ }
});
router.post('/', multer({ storage }), (req, res) => {
// you can store all these params
const fileAttrs = {
fieldname: req.file.fieldname,
originalname: req.file.originalname,
encoding: req.file.encoding,
mimetype: req.file.mimetype,
destination: req.file.destination,
filename: req.file.filename,
path: req.file.path,
size: req.file.size
}
// save fileAttrs into your database (e.g. using Photo model)
Photo.create(fileAttrs).then(() => {
// handle response
res.redirect('/');
});
});
The main idea is that you may want to create the storage middleware and pass it to your route handler. Once an image is uploaded you should have access to a number or attributes that can be stored in the database and used later to retrieve the image.
As for storing the attributes in the database, I'm going to assume that you're using some sort of ORM such bookshelf or sequelize. In which case, you'll have a photo/image model that can be used to write those attrs to the database.
All of that needs to be done in your route handler before handling the response.