Image uploaded in my folder without extension with node js and multer - javascript

I have a weird problem with node and multer.
My script to upload an image is working, but the images are being uploaded without the extension (jpeg, png, etc).
Just like that:
4a8a1400d04b5ccdfdb829aa5ee62763
If I add an extension (jpeg) to the image, then it's displayed.
My code is like that:
const multer = require('multer');
var upload = multer({ dest: __dirname +'/uploads/' });
app.post('/test.html', upload.single('avatar'),function (req,res,next){
console.log("minetype"+req.file.mimetype);
res.render('test', {})
});
And my form:
<form action="/test.html" method="post" enctype="multipart/form-data" name="monForm">
<input type="file" name="avatar" />
<input type="submit" value="Upload">
</form>

you can configure filename with this for exemple :
which will preserve the original extension and still generate a random name using uniqid package. (feel free to replace it with something else if needed)
const uniqid = require('uniqid');
const path = require('path');
const multer = require('multer');
let upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, `${__dirname}/uploads/`);
},
filename: function (req, file, cb) {
cb(null, uniqid() + path.extname(file.originalname));
},
}),
});

Related

How to call a multer upload function outside of handler

I would like to dynamically set the storage destination for uploading a file using multer in my express app, as well as the name of the file.
I have an express route that handles a post request as shown:
EJS:
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="somename">
<select name="p">
<option selected>ABC</option>
<option>DEF</option>
</select>
<input type="number" name="dtYear" value="2020">
<button type="submit">Upload</button>
</form>
ExpressJS:
let storageDest = './public/content/dts/' ;
let fileName;
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, storageDest)
console.log(file);
},
filename: (req, file, cb) => {
if (fs.existsSync(storageDest + file.originalname)){
fileName = file.fieldname + Date.now() + path.extname(file.originalname)
} else{
fileName = file.originalname;
}
cb(null, fileName)
}
})
const upload = multer({storage: storage})
app.post('/upload', upload.single('somename'), (req, res, next) => {
storageDestiantion += req.body.p.toLowerCase() + '/';
fileName = req.body.dtYear;
res.send('Thank you for uploading!')
})
However, I can only change the storageDestination and fileName variables before the multer upload.single() function is called for them to have any effect, as if I change them after the function call, the file is already uploaded.
Is there a way to call the upload.single() function after making the above changes, or does it have to be a parameter of the app.post() function - if it does, is there a workaround to achieve what I desire?

File not uploading in Express JS using Multer

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.

Custom file name from frontend in Multer

I'm uploading a file using FormData and receiving it server-side using Multer. Everything works as expected, except since I'm using FileSystem API on the front-end (https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry), the files I'm uploading come from sub-directories. Multer seems to only see the filename, even if I explicitly set an alias for the file as I append it to form data (https://developer.mozilla.org/en-US/docs/Web/API/FormData/append). It also seems like Multer performs its logic prior to the rest of my request handler and does not see the parameters I set on the body. How do I get multer to see the full path?
Here is a simplified version of what I currently have setup:
Client (alias represents full name with path, file.name is the base name automatically set by FileSystem API):
function upload(file, alias) {
let url = window.location.origin + '/upload';
let xhr = new XMLHttpRequest();
let formData = new FormData();
xhr.open('POST', url, true);
return new Promise(function (resolve, reject) {
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
resolve(file.name);
}
else if (xhr.readyState == 4 && xhr.status != 200) {
reject(file.name);
}
})
formData.append('file', file, alias || file.name); // this should in theory replace filename, but doesn't
formData.append('alias', alias || file.name); // an extra field that I can't see in multer function at all
xhr.send(formData);
});
}
Server:
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
// neither req nor file seems to contain any hint of the alias here
cb(null, file.originalname);
}
});
const upload = multer({storage: storage});
const bodyParser = require('body-parser');
app.use(bodyParser.json());
app.post('/upload', upload.single('file'), function (req, res, next) {
// by this time the file seems to already be on disk with whatever name multer picked
if (req.file) {
res.status(200).end();
} else {
res.status(500).end();
}
});
In order to get this to work, use the preservePath option when configuring multer. The following will work:
const upload = multer({storage: storage, preservePath: true});
However, it's important to note, multer will not create the directories or subdirectories. Those have to be created beforehand. (I tested this too. If directories are created and empty, upload succeeds, however, if directories do not exist, uploads fail).
In their readme, they say:
"Note: You are responsible for creating the directory when providing destination as a function. When passing a string, multer will make sure that the directory is created for you."
A follow-up to that note would be: "you are responsible for creating any sub-directories too".
The relative paths of files uploaded will be accessible in originalname property. So, backend would look like this: (as you had it, but with updated comments)
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
// If you uploaded for example, the directory: myDir/myFile.txt,
// file.originalname *would* be set to that (myDir/myFile.txt)
// and myFile.txt would get saved to uploads/myDir
// *provided that* uploads/myDir already exists.
// (if it doesn't upload will fail)
// /* if( [ uploads/myDir doesn't exist ] ) { mkdir } */
cb(null, file.originalname);
}
});
Helpful tip:
On the front end, I found it easier to test directory / subdirectory upload with: (tested on Chrome latest ok)
<form action="/uploads/multipleFiles" method="post" enctype="multipart/form-data">
<input type="file" name="multiple" webkitdirectory accept="text/*" onchange="console.log(this.files)" />
<input type="text" name="tester" value="uploadTester" />
<input type="submit"/>
</form>
If you want to upload Passport image as a front and back side then pass parameter from frontend like this user:"username" and type:"front" OR type:"back"
then Use it in node side like this
const upload = multer({
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/passport/');
},
filename: function (req, file, cb) {
cb(null, req.body.user+"-"+req.body.type+".jpg");
}
})
});

Upload image to MongoDB with Nodejs and express

I'm trying to upload a image using multer in nodejs. I configured multer to save uploaded images in the "upload" directory and when the form is submitted, the image is sent to the directory as it should. But I'm trying to save the path to that image (req.file.path) to a mongodb database and this is not working. I'm new to nodejs and mongodb and I can't figure out what the problem is.
This is the error I get:
ObjectParameterError: Parameter "obj" to Document() must be an object, got assets\uploads\uploaded_file-1518264794720.jpg
at new ObjectParameterError (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\error\objectParameter.js:23:11)
at model.Document (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\document.js:55:11)
at model.Model (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\model.js:59:12)
at new model (C:\xampp\htdocs\projets\NodeApp01\node_modules\mongoose\lib\model.js:3750:13)
Here is my index.js:
var express=require('express');
var app=express();
var fs=require('fs');
var multer=require('multer');
app.set('view engine','ejs');
app.use(express.static('assets'));
var mongo=require('mongoose');
mongo.connect('mongodb://username:pass#database...');
var schema=new mongo.Schema({
item:String
});
var model1= mongo.model('todomod',schema);
var storage = multer.diskStorage({
destination: 'assets/uploads/',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now()+ '.jpg')
}
})
var upload = multer({ storage: storage });
app.get('/',function(request,response){
response.render('index.ejs');
});
app.get('/add_item',function(request,response){
response.render('add_item.ejs');
});
app.post('/add_item',upload.single('uploaded_file'),function(req,res){
console.log(req.body);
console.log(req.file);
model1(req.file.path).save(function(err,data){
if(err) throw err
res.json(data);
})
});
app.listen(80);
console.log('Application listening on port 80....');
And here is my view (.ejs) for the form:
<body>
<% include partials/navigation.ejs %>
<h1>Adding an item to the shop</h1>
<form id="theform" action="add_item" method="POST" enctype="multipart/form-data">
Name of item:<input type="text" name="item" class="thedata"><br>
<br>
Photo of item:<input type="file" name="uploaded_file" class="thefile">
<button type="submit">Add item</button>
</form>
</body>
The issue is you are passing uploaded file path instead of file object, You can do like this to save uploaded file path in mongodb:
let db_data = {
item : req.file.path
};
model1(db_data ).save(function(err,data){
if(err) throw err
res.json(data);
})

req.file is undefined (multer, node.js)

I've been trying to upload an image for a while now, but req.file is still undefined. Can someone see why?
this is my page. I am able to pick an image when I click the '+' glyphicon, but on the server side req.file is still empty.
EJS file
input[type="file"] and input[type="submit"] have css styles display: none
<form action="/profile/addProfilepicture" method="post" id="form" enctype="multipart/form-data">
<span id="upload" class="glyphicon glyphicon-plus-sign"></span>
<label for="profilePic"></label>
<input id=profilePic type='file' />
<input type="submit">
</form>
<img class="profileImg"
src="<%="images/pexels-photo-370799.jpeg"%>"
alt="fail">
Client JS file
When I click the '+'glyphicon it lets me pick an image. When I do this, this will trigger the form to submit and send a post request.
$("#upload").on('click',function() {
$("input[type='file']").click();
});
$('input[type="file"]').change(function (e) {
$("input[type='submit']").click()
});
server side JS
On the server side it stops at:
TypeError: Cannot read property 'filename' of undefined
at C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\routes\profile.js:27:38
at Immediate._onImmediate (C:\Users\Tijl Declerck\Desktop\projects\digitalNomadApp\node_modules\multer\lib\make-middleware.js:53:37)
at runCallback (timers.js:793:20)
at tryOnImmediate (timers.js:751:5)
at processImmediate [as _immediateCallback] (timers.js:722:5)
The console.logs I tried gave me this: req.body returns an empty object and req.file returns undefined.
var express = require('express');
var router = express.Router();
var multer = require('multer');
var User = require('../models/Users');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './public/uploads/profilePics')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({ storage: storage }).single('myImage');
router.post('/addProfilePicture', function (req, res) {
var profilePicUrl = '';
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
} else {
console.log(req.file);
profilePicUrl = req.file.filename;
User.update({username: req.user.username}, {'profilePic.uploaded': true, 'profilePic.link': profilePicUrl}, function(err, doc){
console.log('THIS IS DONE')
});
}
});
});
You have to provide a name to your file input and it should match the single method's name, this is from multer doc:
.single(fieldname)
Accept a single file with the name fieldname. The single file will be
stored in req.file.
This is not well documented but fieldname refers to input name attribute
EJS file
<input id='profilePic' name='myImage' type='file' />
Express
...
var upload = multer({ storage: storage }).single('myImage');
...

Categories