This question already has an answer here:
Sending a CSV file from browser to nodejs server
(1 answer)
Closed 2 years ago.
On my frontend application, I have a form where a user can upload a file. I want to upload this file to my Node.js backend using the fetch API.
Frontend code
HTML:
<form id="forecast-form">
<input type="file" id="csvFile" />
<input type="submit" value="Submit" />
</form>
JavaScript:
const handleSubmit = (e) => {
e.preventDefault();
const csv = e.target.csvFile.files[0];
console.log(csv); // This actually logs the file object
const formData = new FormData();
formData.append('csvFile', csv);
const options = {
method: 'POST',
body: formData,
};
fetch('http://localhost:4000/api/v1/forecast', options)
.then((res) => res.text())
.then((data) => console.log(data)); // output: {}
}
Node.js + express code
create: async (req, res, next) => {
try {
const body = req.body;
console.log(body); // output: {}
res.status(201).send(body);
next();
} catch (err) {
next(err);
}
}
But the only output I get, both from the frontend and backend, is an empty object {}.
Edit:
I wouldn't say this question is a duplicate since the aforementioned question in mind is not specific to the fetch API, which this question is.
After finding some information from this question Sending a CSV file from browser to nodejs server
I realised I couldn't just use vanilla req.body like I do for json data, instead I need to use some sort of middleware to handle the multipart/form-data content type.
So I used the multer middleware
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
router.post('/forecast', upload.single('csvFile'), forecast.create);
Then on POST, the file will be uploaded under uploads/ and per multer documentation:
// req.file is the avatar file
// req.body will hold the text fields, if there were any
Related
I want to store the image uploaded in a simple html input
<input
type="file"
name="thumbnail"
id="image-input"
accept="image/png, image/jpg, image/jpeg"
hidden
/>
This leads to a post request when submitted
router.post('/dish/new',async (req,res) => {
let { name, desc, cost, isnonveg, thumbnail} = req.body;
fs.writeFile(path.join(__dirname,'../userdata/images/dishes','g.jpg'),thumbnail, ()=>{
console.log('image stored')
})
})
I want to store whatever the user uploads using node fs
How can I go about doing this.
The approach I use here merely stores the name of the file instead of the actual file.
if you are using express you can add this attribute to form tag enctype="multipart/form-data". Then your image will be sent from frontend . To receive the image in the server you need to configure a middleware called express-fileupload
var fileupload = require("express-fileupload");
then you can register the middleware by app.use(fileupload()); in your app.js file
in your route file
router.post('/something', async (req, res) => {
let image=req.files.fieldName
})
image.mv('./public/images/foldername/' + 'imageName' + '.jpg', (err) => {
if (err) {
console.log(err)
} else {
res.redirect('/)
}})
once it is done when you submit your form you can see the uploaded image in your server /public/images/foldername .
I am using React with Nodemailer to send emails from a user input form, the user should be able to attach a file (a PDF for example) through the form and the content of the form will be sent as an email using Nodemailer. My issue comes with not knowing how to attach the file to the email. Here is a list and examples of properties that can be used using Nodemailer. What properties can I extract from the object inputted through the file input to the event.target.files to use to attach to the email, can I get the path of the inputted file for example?
Code:
const [file, setFile] = useState(null);
const handleSubmit = async(e) => {
e.preventDefault();
try {
await axios.post("http://localhost:4000/send_form", { file });
}
catch (error) {
console.log(error);
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="file"
onChange={(e) => setFile(e.target.files[0])}
required/>
<button type="submit">Send</button>
</form>
);
Server:
app.post("/send_form", cors(), async (req, res) => {
let { file } = req.body;
await transport.sendMail({
from: "from#sender.com",
to: "to#receiver.com",
subject: "Subject",
html: `<h1>Hello</h1>`,
attachments: [{
filename: "",
path: ""
}]
})
});
You don't need axios to upload the file, just POST it as FormData with the Fetch API.
async function handleSubmit(event) {
event.preventDefault();
let fd = new FormData();
fd.append('myfile', file);
fetch('http://localhost:4000/upload', {
method: 'POST', body: fd
}).catch(err => {
console.error(err);
});
}
If you have other data to submit along with your image it can also be append to the FormData object.
fd.append('name', 'Joe');
fd.append('age', 40);
// etc...
Or, you can simply capture all fields from any HTMLFormElement. Just make sure to set the enctype attribute to be multipart/form-data.
let form = document.querySelector('#your-form');
let fd = new FormData(form);
Then, on the server you can use the multer middleware to stream the file buffer to the nodemailer attachment:
import express from 'express';
import multer from 'multer';
import transport from './your_app.js'
const app = express();
const upload = multer({
storage: multer.memoryStorage()
});
app.post('/upload', upload.single('myfile'), (req, res) => {
transport.sendMail({
from: "from#sender.com",
to: "to#receiver.com",
subject: "Subject",
html: `<h1>Hello</h1>`,
attachments: [{
filename: req.file.originalname,
content: req.file.buffer
}]
})
});
app.listen(4000);
If you have other middleware you need to use on this route, they can be passed in as an array:
import cors from 'cors';
let middleware = [
cors(),
upload.single('myfile')
];
app.post('/upload', middleware, handler);
Note that the key used in the following two statements must match. This key corresponds to the name attribute of the file input.
In handleSubmit() :
fd.append('myfile', file);
In app.post() :
upload.single('myfile')
Multer also allows for multiple file uploads if needed. You can either capture several files from a single input with the multiple attribute:
upload.array('myfile', 3)
Or you could use several file inputs:
upload.fields([
{ name: 'myfile', maxCount: 1 },
{ name: 'another-file', maxCount: 8 }
])
If you do this, you will need to access the uploaded file data from the req.files property instead of the singular req.file.
The rest of your form data will be available in the req.body object:
req.body.name == 'Joe'
req.body.age == 40;
I am using body-parser, multer and express in my mern stack app. I need to upload a file as well as some text from the form. I am sending the post request as follows:
function addPost(postdetail){
axios.post('http://localhost:4000/blog',postdetail,{ headers:{
"Content-Type": "multipart/form-data"
}}).then(res=>{
dispatch({
type:'ADD_DATA',
payload:res.data,
})
}).catch(error=>{
console.log(error)
})
}
When I add the header I get an empty object as the output of req.body. But when the multipart/form-data is not added I get other req.body but empty only for the file.
I have used the react hooks to set the state and appended the file using FormData. When the appended value is logged I get all the information of the uploaded file. So, I could know that the file is properly appended.
const Addpost=()=> {
const [title, settitle] = useState('')
const [des, setdes] = useState('')
const [file, setfile] = useState(null)
const {addPost}=useContext(Globalcontext)
const handleSubmit=(e)=>{
e.preventDefault()
const formData = new FormData()
formData.append('file', file[0])
console.log([...formData])
const addedValue={
title,
des,
formData
}
addPost(addedValue)
settitle('')
setdes('')
setfile('')
}
return (
<div>
<form onSubmit={handleSubmit}>
<input type="text" name="title" value={title} onChange={(e)=>settitle(e.target.value)}/>
<input type="text" name="des" value={des} onChange={(e)=>setdes(e.target.value)}/>
<input type="file" name="file" onChange={(e)=>{setfile(e.target.files)}} />
<button type='submit' value='submit'>Add Post</button>
</form>
</div>
)
}
export default Addpost
This is how I am using multer in the server side:
const express=require('express')
const multer=require('multer')
const path=require('path')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, __dirname+'/file/uploads/')
},
filename:function(req,file,cb){
cb(null,file.fieldname+path.extname(file.originalname))
}
})
const upload=multer({
storage:storage
})
exports.addPost=(upload.any(),(req,res)=>{
const post=new Blog()
post.title=req.body.title
post.des=req.body.des
post.file = req.file
post.save((err,doc)=>{
if(!err){
res.send(doc)
}
else{
console.log(err)
}
})
console.log(req.body)
})
I think the problem is only with the server-side where I am trying to upload the file using multer. After I have used the header in the axios post request I am not able to insert any information to the mongoose and I get {} for consoling req.body and other fields are undefined.
Since you are using multer.any(), an array of files will be stored in req.files (see https://www.npmjs.com/package/multer#any).
In your case you can simply use multer.single() as you're uploading a single image - the file will then be available under req.file, rest of the form data will be available under req.body:
exports.addPost=(upload.single('file'),(req,res)=> { ... });
I have a pdf generated file on the front end of my application that I got using html2pdf plugin. I have a nodemailer server at my backed where I can attach files and send it to an email of my choice. Is there a way I can send the pdf that is generated in the front end to Nodejs? I am also using express
Edit:
Based on your advice I did
**On the Client side**
var element = document.getElementById('element-to-print');
const elem = document.getElementById('html');
html2pdf().from(element).toPdf().get('pdf').then(function (pdf) {
window.open(pdf.output('bloburl'), '_blank');
var formData = new FormData();
formData.append("filename", pdf);
axios.post('/upload',formData).then(res => { console.log(res) })
// formData.append("uploadedFile", fileInputElement.files[0]);
})
On express app
app.post('/upload', fileUpload(), function(req, res) {
const sampleFile = req.files.uploadedFile;
// do something with file
res.send('File uploaded');
})
But I get this error coming from index.js
TypeError: Cannot read property 'uploadedFile' of null
Yes.
Create and endpoint/route in your express app
Use a http agent like superagent, request or axios in your client
Use multipart form or something like FormData to create the data that is supposed to be sent.
Post it to the url you created in express.
Use middlewere such as express-fileupload or busboy to handle the attachment.
So in your client. You have something like
var formData = new FormData();
formData.append("filename", "My awesome file");
formData.append("uploadedFile", fileInputElement.files[0]);
Then you post that with something like Axios
axios.post('/upload',formData).then(res => { console.log(res) })
In your express app you do something like
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
app.post('/upload', fileUpload(), function(req, res) {
const sampleFile = req.files.uploadedFile;
// do something with file
res.send('File uploaded');
})
<form method="post" enctype="multipart/form-data" action="/">
<div>
<label for="profile_pic">Choose file to upload</label>
<input type="file" id="profile_pic" name="profile_pic"
accept=".pdf">
</div>
<div>
<button>Submit</button>
</div>
For receive it at node you have to define a new route at the same path.
I am sending a CSV file to my node + express server using jQuery's ajax POST method. When I post the csv file to my express route, I don't know how to access the file once it reaches the server. My goal is to pass the CSV file into my route's middleware called upload.single(). I'm also not sure which data type to specify for the ajax call.
Here is my form where I accept the CSV file:
<form onSubmit={this.handleSubmit.bind(this)} encType="multipart/form-data">
<input id="userFile" type="file" name="userFile"></input>
<input type="submit" name="submit" value="Upload New Candidates"></input>
</form>
Here is my handleSubmit function which makes the POST request:
handleSubmit(event) {
event.preventDefault();
var csvToSend = document.getElementById("userFile").files[0];
$.ajax({
type: "POST",
url: 'http://localhost:3000/',
data: csvToSend,
success: success,
dataType: //not sure what to put here
});
}
Here is my express route on the server. How should I access the CSV file sent from the client and enter it into the upload.single() middlware?
app.post('/', upload.single('userFile'), function(req, res, next) {
res.sendStatus(200);
});
As peteb mentioned it looks like you're using multer for a single file and so in that situation:
// If you set up multer to store files locally
const upload = multer({ dest: 'uploads/' });
const fs = require("fs");
app.post('/', upload.single('userFile'), async function(req, res, next) {
const filePath = req.file.path;
const fileData = await fs.promises.readFile(filePath); // Buffer of csv file
const csvString = fileData.toString();
// Do whatever you need to with the csv file
res.sendStatus(200);
});