I've been following some tutorials on how to upload a file using node and I've had success in actually uploading the file (I've primarily used https://www.geeksforgeeks.org/file-uploading-in-node-js/). However, every time it uploads the file, it changes to a new page. Is there a way to stay on the same page after uploading the photo, or do I have to create an HTML file with the same name with the same HTML as before the upload? The working code is below:
const express = require("express")
const path = require("path")
const multer = require("multer")
const app = express()
app.use(express.static(__dirname + '/public'));
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads")
},
filename: function (req, file, cb) {
cb(null, file.fieldname + "-" + Date.now()+".jpg")
}
})
var upload = multer({
storage: storage,
fileFilter: function (req, file, cb){
var filetypes = /jpeg|jpg|png/;
var mimetype = filetypes.test(file.mimetype);
var extname = filetypes.test(path.extname(
file.originalname).toLowerCase());
if (mimetype && extname) {
return cb(null, true);
}
cb("Error: File upload only supports the "
+ "following filetypes - " + filetypes);
}
}).single("myfile");
app.get("/",function(req,res){
res.render("Signup");
})
app.post("/uploadCode",function (req, res, next) {
upload(req,res,function(err) {
if(err) {
res.send(err)
}
else {
res.send("Success, Image uploaded!")
}
})
})
app.listen(8000,function(error) {
if(error) throw error
console.log("Server running on port 8000")
})
If you do the upload from a form where the browser submits the form for you automatically via a submit button, then it will be a browser form POST and the browser will automatically display whatever that POST request returns as the response.
If you, instead, upload form a form with a Javascript Ajax call to do the form POST, then the form response just comes back to your Javascript and nothing happens to the current web page in the browser. Your Javascript can receive that response and then decide what to do with it (if anything).
You've shown your server-side code here, but it's actually the client-side that controls what happens after the response from the upload is received back from the server. So, the changes would have to be made on the client-side to submit your form via Javascript.
Related
I am working on an MEAN Stack application and I am trying to manage a form that allows users to upload a file when they submit. It appears to be working on the client side, however when I send the post request from the client and inspect the request body the file is an empty object. It is just a small .docx file so it should be fine size-wise. But I do not understand why nothing is properly received since the request goes through with out error. I was under the impression that files could be sent this way.
Am I missing something?
code from angular service
sendApplcation(data : any): Observable <any>
{
return this.http.post(this.url+ '/careers/api/application', data);
}
nodejs code
router.post("/api/application", (req, res) => {
const application = req.body;
console.log(req.body.file);
let email = {
to: `${req.body.email}`,
from: "Careers#TrueLogistics.ca",
subject: "Application Recieved",
text: JSON.stringify(req.body),
html: `<p> ${JSON.stringify(req.body)} </p>`,
};
mailer.sendMail(email, (err, res) => {
if (err) {
console.log(err);
}
});
email.to = "mjayfalconi#gmail.com";
mailer.sendMail(email, (err, res) => {
if (err) {
console.log(err);
}
});
res.json("Applcation Submitted Successfully!");
});
Check out the multer package on npm.
File upload works a bit differently than the normal request.
You will also set enctype to multipart at the front end.
Furthermore, I see you are using nodemailer to send the file as an attachement. Read the documentation about the attachment. You don't send the file that way.
//Dependencies
const multer = require('multer');
//Multer DiskStorage Config
const diskStorage = multer.diskStorage(
{ destination: 'assets/profile_upload'} );
//Create Multer Instance
const upload = multer({ storage: diskStorage });
//File upload
//or app.post()
router.post('/upload-file', upload.single('file'), (req, res) => {
//The file
console.log(req.file)
;});
//Your code:
app.post('/upload', (req, res) => { ... try doing app.post('/upload' ,upload.single('file'),
Also check out this post: https://stackoverflow.com/a/61341352/9662626
Sorry for the bad formatting. I only have access to my phone at the moment.
I am using express-fileupload to upload the images. The images are saved in my local directory. I want to insert the name of the file to the mongodb if possible. Finally I want the image to be displayed in my frontend.
function insertRecord(req,res){
if(req.files){
const file=req.files.filename
filename=file.name
file.mv("./upload"+filename,function(err){
if(err)
console.log(err)
})
}
const user=new User()
user.name=req.body.name
user.address=req.body.address
user.email=req.body.email
user.mobile=req.body.mobile
user.filename=req.body.filename
user.save((err,docs)=>{
if(!err){
res.redirect('/user/list')
}
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("./users/addOrEdit", {
viewTitle: "Insert User",
user: req.body
});
}
else
console.log('Error during record insertion : ' + err);
}
});
}
I am not sure whether the way to insert the name of the file to the mongodb is correct or not. Anyway, that is optional but I am not understanding how can I display the uploaded images which are present in the local directory.
I tried to save the image as base64 but the record is not saved to the database now.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
router.post('/',upload.single('myImage'),function(req,res){
if (req.body._id == '')
insertRecord(req, res);
else
updateRecord(req, res);
})
function insertRecord(req,res){
var img = fs.readFileSync(req.file.path);
var encode_image = img.toString('base64');
var finalImg = {
contentType: req.file.mimetype,
image: new Buffer(encode_image, 'base64')
};
const user=new User()
user.name=req.body.name
user.address=req.body.address
user.email=req.body.email
user.mobile=req.body.mobile
user.save(finalImg,(err,docs)=>{
if(!err){
res.redirect('/user/list')
}
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("./users/addOrEdit", {
viewTitle: "Insert User",
user: req.body
});
}
else
console.log('Error during record insertion : ' + err);
}
});
}
Edit: I think there is a problem in the code: it should be `'./upload/'+filename' not without the second slash.
In order to show the images, you have to open a static route in Express. Example: app.use('/images', express.static(PATH)). Then you can, in the frontend, call it as <img src="URL/images/FILENAME" />
From your code, it is not possible to understand what kind of data you are sending to the server. As far as I understand, you're trying mv the string filename. In order to transfer files (such as images), you should have form-data instead of JSON data or you should encode the image file into Base64 to transfer it as text (not the filename, the whole file).
Check Multer out for this kind of job. It is described well in the README.md. Apart from that, until you submit the form, the image won't be available in the front-end. If you want to preview the image before uploading it's a separate process which you can learn more in here.
SOLVED THANKS TO #Patrick Evans
I am creating my own web project and i need some help.
At the website, the client is requested to upload a face photo.
Then , when the client presses "upload" button , his photo is sent with a request to "face++" api which gives back details about the photo such as emotions and gender, at a different ejs page. At the new page the client sees his photo and below are the details about his photo.
It works fine , but when the client gets back to the homepage, and chooses a different new photo, then presses upload , he sees his new photo that he chose, but gets the same details as were at the last photo (details from face++ api).
I use the following:
express.
unirest for making the request to "face++" api.
cloudinary for having a url, and using the url at the face++ request(the url represents the client's photo).
multer for storing at local drive.
When i print out the details which return from "face++" api , at the "requestFromApi.end()" function , i already notice the details havent changed from prior request, but i do notice at cloudinary that a different photo was uploaded.
I attached my app.js code.
Thanks alot for any help :)
var unirest = require("unirest");
var requestFromApi = unirest("POST", "https://faceplusplus-
faceplusplus.p.rapidapi.com/facepp/v3/detect");
var cloudinary = require("cloudinary").v2;
const express = require('express');
const multer = require('multer');
const app = express();
const path = require("path");
var bodyParser = require("body-parser")
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
limit: '50mb',
extended: true
}));
app.set("view engine", "ejs");
cloudinary.config({
cloud_name: 'dkqvnprcj',
api_key: '756886954695832',
api_secret: 'you know i cant give you that...'
});
app.get("/", function (req, res) {
res.render("FaceApp.ejs");
});
// SET STORAGE
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
}
})
var upload = multer({ storage: storage })
app.post('/upload', upload.single('photo'), (req, res) => {
if (req.file) {
cloudinary.uploader.upload(req.file.path, function (error, result) {
//console.log(req.file);
let result_ = result;
let url = result.url;
//console.log(url)
requestFromApi.query({
return_attributes: "gender,age,smiling,facequality,eyestatus,emotion,ethnicity,beauty,skinstatus",
image_url: url
});
requestFromApi.headers({
"x-rapidapi-host": "faceplusplus-faceplusplus.p.rapidapi.com",
"x-rapidapi-key": "9dd7fa4266mshf1c29ba307ecf2dp1bb1dajsna431d00b6273",
"content-type": "application/x-www-form-urlencoded"
});
requestFromApi.form({});
requestFromApi.end(function (result) {
if (result.error) throw new Error(result.error);
else {
let detailsFromApi = JSON.parse(JSON.stringify(result.body.faces));
detailsFromApi.forEach(function (element) {
console.log(element);
});
res.render("image",{result_ : result_, detailsFromApi:detailsFromApi});
}
});
});
}
else throw 'error';
});
SOLVED THANKS TO #Patrick Evans [1]: https://stackoverflow.com/users/560593/patrick-evans
I had to make sure i call "Unirest" at every single Post request, and not just at the beginning of the execution.
My uploaded form has a file and a field "filepath" which is dynamically generated and contains the desired filepath of the file I'm uploading.
Example:
filepath: "assets/images/asset-01-02/"
I'm trying to set this field as a variable so I can save the file to this directory to keep uploaded files organized.
Current code:
const cors = require('cors');
const express = require('express');
const app = express();
const formidable = require('formidable');
app.use(cors());
app.options('*', cors());
app.post('/upload', (req, res) => {
var form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
if (err) {
console.log('Error', err)
throw err
}
console.log(fields.filepath); //Output e.g. "assets/images/asset-01-02/"
})
form.on('fileBegin', (name, file) => {
//Need to use fields.filepath after '/public/uploads/'
//e.g. __dirname + '/public/uploads/' + fields.filepath + file.name;
file.path = __dirname + '/public/uploads/' + file.name;
});
form.on('file', (name, file) => {
console.log('Uploaded ' + file.name);
});
});
app.listen(80, function(){
console.log('Listening on Port 80...');
});
I need to get the fields.filepath value passed to the form.on('fileBegin') function but I'm not sure how to. I haven't come across any examples of this specific issue.
As far as I am understanding your question, you are trying to send 'filepath' with 'multipart/form-data' or from client to server with uploaded files.
and you are trying to catch and set this 'filepath' using form.parse 'fields' into form.parse callback.
your code do not work simply because form.on('fileBegin') callback will execute before form.parse callback.
here is execution order for callback in Formidable.(can change due to asynchronous functions)
form.on('fileBegin')
form.on('file')
form.parse()
form.on('end')
instead passing 'filepath' with 'form-data', send it with query like localhost/upload?filepath=assets/images/asset-01-02
and get it like
var filepath = req.query.filepath;
in your code
I use multer to parse multiple files sent as multipart/data-form with axios
...
const storage = multer.diskStorage({
destination: './gallery',
filename(req, file, cb) {
(1) ....
},
});
const upload = multer({ storage });
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
(2) ...
})
.catch(..)
})
...
at this point everything is fine and my images are saved.
the problem is that i want to make a loop in (1) or (2) and name my files like this
files.forEach((file, index) => {
// rename file to => product_id + '_' + index + '.jpeg'
}
For example if i have 3 files they will be named to
5a9e881c3ebb4e1bd8911126_1.jpeg
5a9e881c3ebb4e1bd8911126_2.jpeg
5a9e881c3ebb4e1bd8911126_3.jpeg
where 5a9e881c3ebb4e1bd8911126 is the id of the product document saved by mongoose.
how to solve this naming issue ?
is multer the best solution cause i want full control over my files ?
Is there a better approach with another node package ?
is it good to send images as multipart/data-form or data URL base64 ?
This is easy, as long as you understand how express works. So before jumping to solution its important to have a clear understanding.
When you have a express code like below
router.post('/abc', function(req, res) {res.send('hello world');})
Express passes the request from chains of middlewares/functions. Now each function gets req, res, next parameters. The next is function, which a middleware is suppose to call when the processing is complete. If the middleware decides not to call next the request ends there and no more middlewares are called further.
When we used function(req, res) {res.send('hello world');}, we didn't take the next parameter at all, which means we are not interested in any other code to do anything. Now getting back to our problem
router.post('/products', upload.array('images'), (req, res, next) => {...}
You have used upload.array('images') first and then your actual product creation code. So I would show two approaches to solve this problem
One more middleware to rename the files
router.post('/products', upload.array('images'), (req, res, next) => {
Product.create(...)
.then((product) => {
req.product = product
next();
})
.catch(..)
}, (req, res, next) => {
//Get the product id using req.product
//Move the files as per the name you desire
})
Reverse the processing order
In this approach you first create the product and then let image processing happen. I have created a sample for the showing the same
let express = require('express');
let bodyParser = require('body-parser');
app = express();
let multer = require('multer');
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
console.log('Product id - ' + req.product_id);
cb(null, req.product_id + '.js');
},
});
const upload = multer({ storage });
app.all('/', (req, res, next) => {
console.log('Hello you');
promise = new Promise((resolve) => {
// simulate a async product creation
setTimeout(() => resolve(1234), 100);
});
promise.then((product_id) => {
console.log('create the product and get the new product id')
// set the product id in the request object, so the multer
// filename function can access it
req.product_id = product_id;
res.send('uploaded files');
if (next)
next();
});
}, upload.array('images'));
module.exports = {
app
};
app.listen(8020);
And testing it using postman works fine
Edit: 19-Mar-2018
For multiple files you can easily update your filename function code like below
const storage = multer.diskStorage({
destination: './gallery',
filename: (req, file, cb) => {
req.file_id = req.file_id || 0;
req.file_id++;
console.log('Product id - ' + req.product_id);
cb(null, req.product_id +'_' + req.file_id +'.js');
},
});
This will make sure that you get all the files for that product. Now coming to your questions
how to solve this naming issue ?
This answer already does that
is multer the best solution cause i want full control over my files ?
I can't say, as long it works and does what you want, it should be good enough
Is there a better approach with another node package ?
I couldn't find lot of packages. But you can explore this if you want
is it good to send images as multipart/data-form or data URL base64 ?
I would use multipart/data-form, so that no base64 conversion is needed at client side. But again this is a matter of opinion as well.
You can't set the name purely in (1) since at that point you do not know the ID of the product yet.
You can't set the name purely in (2) since at that point the files have already been saved (with filename generated by your filename(req, file, cb) function).
So I think the best solution might be to move the files after they are uploaded.
This could be done in (2). When you process the files in the router, req.files will be an array of files that have already been uploaded.
In your promise callback for Product.create, you have access to the product (which you need for the ID) and the list of files (which you need for the number).
For that, you could use fs.rename(oldPath, newPath, callback).
https://nodejs.org/docs/latest/api/fs.html#fs_fs_rename_oldpath_newpath_callback
Something like this should work:
Product.create(...).then((product) => {
req.files.forEach((file, index) => {
// file.path is the full path to the file that was uploaded.
// newPath is where you want to put it.
// Let's use the same destination and just change the filename.
const newPath = file.destination + product.id + '_' + index
fs.rename(file.path, newPath)
})
})