Uploading image and text using multer in mern stack - javascript

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)=> { ... });

Related

How to store an html input image file to a file locally on the server

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 .

How to attach file using Nodemailer in React.js

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;

Missing Json Data while uploading axios to node js

My code in not functioning sometimes.
here is my Upload.jsx
function FileUpload() {
const [file, setFile] = useState();
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("file", file);
axios
.post("http://localhost:5000/form", formData)
.then((res) => console.log(res.data))
.catch((err) => console.log(err));
};
return (
<Form onSubmit={handleSubmit}>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Default file input example</Form.Label>
<Form.Control
type="file"
onChange={(e) => setFile(e.target.files[0])}
/>
<Button variant="primary" type="submit">
Submit
</Button>
</Form.Group>
</Form>
);
}
and here is my app.js
const fileupload = require('express-fileupload')
app.use(express.json({limit:'5000mb'}));
app.use(express.urlencoded({ extended: false, limit:'5000mb', parameterLimit:50000000 }));
app.post("/form", (req, res) => {
fs.writeFile(req.files.file.name, req.files.file.data, (err)=>{
err && console.log(err);
!err && console.log('completed');
})
})
Everything is fine when I am uploading any other files excepts json file. File size doesn't matter. I am able to upload 900mb of file without any issue.
the problem is when I am uploading a json file size of 250mb(or any sized) only, its missing data and saved to the server only 12mb - 35mb(randomly sized).
And sometimes fully uploaded and saved without any missing data.
I don't understand what I am missing
Please help. Thanks is advance.
Can you please send the response back to the application from the server and give a try like this
const fileupload = require('express-fileupload')
app.use(express.json({limit:'5000mb'}));
app.use(express.urlencoded({ extended: false, limit:'5000mb', parameterLimit:50000000 }));
app.post("/form", (req, res) => {
fs.writeFile(req.files.file.name, req.files.file.data, (err)=>{
if(err){
console.log(err);
res.status(500).send(err);
return
}
console.log('completed');
res.status(200).send('ok');
})
})

Upload csv file to Node.js server with fetch API [duplicate]

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

How to send pdf file from front-end to Nodejs server?

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.

Categories

Resources