Sending formdata from React to express using multer - reactjs

This is a MERN app that has a form consisting of text inputs and a file input. The problem: I am unable to access the file input in my backend.
Start of controller function:
async function create(req, res) {
function base64_encode(file) {
let bitmap = fs.readFileSync(file)
return bitmap.toString('base64')
}
console.log(req.file)
let image = base64_encode(req.file.path)
The console.log of req.file returns "undefined" and I get error message "Cannot read properties of undefined (reading 'path')" referring to req.file.path.
The route:
const multer = require("multer")
const upload = multer({ dest: '../../uploads/'})
router.post("/", upload.single('image'), spotCtrl.create)
It should be noted that the image file is not showing up in the image folder I created.
The handleSubmit function:
handleSubmit = async () => {
let chosenImage = this.state.image
const formdata = new FormData()
formdata.append('image', chosenImage)
let body = {
name: this.state.name,
description: this.state.description,
address: this.state.address
}
let options = {
method: 'POST',
body: JSON.stringify(body),
formdata: formdata
}
await fetch("/api", options)
.then(res => res.json())
}
The <form tag has encType="multipart/form" set as well.
I've installed multer, fs, express-fileupload, among others
A couple maybe relevant lines from my server.js:
app.use(express.json(), expressfileupload());
app.use(express.static(path.join(__dirname, 'build')));
I have tried in my controller function to use req.files instead of req.file however I get the same result.

Related

Upload file with React

I want to make a simple file upload form on the front end. Then, on the backend, I would pass the information about that file to an API.
Here is my front-end code where I call a specific function on the back end and pass the data:
import React from 'react';
import Axios from 'axios';
const Upload = () => {
// a local state to store the currently selected file.
const [selectedFile, setSelectedFile] = React.useState(null);
const handleSubmit = async (event) => {
event.preventDefault()
//Got all the Infos about my file
console.log(selectedFile)
const formData = new FormData();
formData.append("selectedFile", selectedFile);
//Empty result
console.log(formData)
Axios.get("http://localhost:3001/upload", {
//I will pass the data to a function in the backend
params: {
data: formData,
},
})
.then((Response) => {
console.log(Response)
})
.catch(function (error) {
console.log(error);
});
}
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0])
}
return (
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleFileSelect}/>
<input type="submit" value="Upload File" />
</form>
)
};
export default Test
On the back-end side, a route call the method
router.get('/upload?', Upload);
Then finally the function in the backend to process
const ApiProcess = (req, res) => {
var axios = require('axios');
var data = req.query
console.log(req.query)
//All the API Stuff
}
But the problem is that I receive empty data in the Backend. What's wrong with my code?
Thanks
EDIT
On backend side I use multer and add 'app.use(multer().any())' on top of index file. That help cause now I cant access in backend to a simple formData. Now my function that receive the data log this '[Object: null prototype] {}'
Any idea ?
This is because your file is not getting forwarded from frontend
use FileReader instead
<input type="submit" value="Upload File" onChange={(e) =>
setFile(e.target.files)} />
const data = new FormData();
data.append(file[0])
and then you can access the file data on file[0] index and after storing the data you can forward it to the backend
there are some problems in your code.
first of all an upload request usually is a post type. and also you should send Content-Type header with your request. so:
Axios.post("http://localhost:3001/upload", formData {
headers: {
'Content-Type': 'Multipart/formData',
},
})
when you log formData it's always empty. you can use some methods like formData.keys() or formData.values() to see inside it.
Ok I got the solution. I missed a piece of middleware to process Multipart/formdata on Express Side :
const router = express.Router();
const multer = require("multer");
//Set the destination folder and Naming of the file that is upload
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({ storage: storage })
Then I process the formData with the files
router.post('/upload', upload.array("file"),Upload);
Thanks a lot for your help

Pass Files to Node Server

Im having this function in my react app, what is does is sending a file to the server and the server save it to the declared dir.
const handleUploadfile = (event) => {
event.preventDefault();
const data = new FormData();
data.append('file',file );
fetch("http://localhost:4000/upload", {
method: 'POST',
body: data
}).then((response) => console.log(response))
}
heres the code in the node server handling the request above
app.post("/upload", (req, res) => {
const newpath = "./files/";
const file = req.files.file;
const filename = file.name;
file.mv(`${newpath}${filename}`, (err) => {
if (err) {
res.status(500).send({ message: "File upload failed", code: 200 });
}
res.status(200).send({ message: "File Uploaded", code: 200 });
});
});
The upload is working well, but what i want to know is, am i able to include some data like a string directory for each uploaded file something like that.
BTW im using express-uploadfile middleware so i can access the files.
If you want to upload another filed in your request you simply need to call append on your FormData object.
const data = new FormData();
data.append('file',file );
data.append('string','some string');
Then read it on the server
const string = req.body.string;

sending more data along with formData to an API with axios

I´m writing an API that loads files in a folder
In order to select the files I’m using FormData. I use
<input type='file' ref={inputElement} onChange={handleChange} />
After choosing the file, In use axios to make a request
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', formData, {…
The api receives it and does it’s thing
app.post('/upload', (req, res) => {
if (!req.files) {
return res.status(500).send({ msg: 'file not specified' });
}
// accessing the file
const myFile = req.files.file;
It works fine.
But, I’d like to send extra info to the endpoint, so, I send the extra info and the formdata to axios:
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
axios
.post('http://localhost:3001/upload', {data: formData, extraInfo: 'more info'}, {
And in the endpoint I write:
app.post('/upload', (req, res) => {
console.log(req.body.extraInfo)
console.log(req.body.data)
extraInfo 'more info', ok, but data is empty, I supposed that data should contain formdata, but it’s empty, what can I do in order to get the formData and the extraInfo at the same time
Thanks in advance
Rafael
Just add the extraInfo to the formData and then send it to your server. You may need to double check how your server wants to get the data.
const uploadFile = () => {
const formData = new FormData();
formData.append('file', file); // appending file
formData.append('extraInfo', "Some Info");// additonal data
axios
.post('http://localhost:3001/upload', formData, {

Send multiple images data through React axios in json format instead of formdata?

I want to send multiple images along with some other fields but I want to send those in JSON format instead of formData.
On the server I am using multer for image storing.
Refer to the link for the React code.
https://codesandbox.io/s/react-hooks-usestate-1h8cw?file=/src/index.js:1017-1023
The images are uploading and displaying but I am not able to find a way to send it on server side in JSON format.
Server-side code
const router = require("express").Router();
const City = require('../../model/city');
const multer = require('multer')
var upload = multer({
dest: 'uploads/'
})
//Post City at this route
router.post('/create', upload.array('Images', 12), async (req, res) => {
console.log(req.files);
const city = new City({
City: req.body.City,
Description: req.body.Description,
Images: req.files.path
});
try {
let check;
check = await City.findOne({
City: req.body.City
});
// res.send(check);
if (check) {
res.send({
"message": "City already exists"
})
} else {
const savedCity = await city.save();
res.send(savedCity);
}
} catch (err) {
res.status(400).send(err);
}
});

Passing file from React app to Node app to upload to S3, file is undefined when defining the upload params?

I'm trying to make my own personal resume using React and Node, and I've been having issues with the upload to AWS S3 for a few hours now.
I fire an event when submitting the form with the file :
onSubmitChange = () => {
const data = new FormData()
data.append('url', this.state.url);
data.append('name', this.state.name);
data.append('description', this.state.description);
data.append('filePath', this.state.file);
fetch('http://localhost:3001/new-experience', {
method: 'post',
headers: {'Content-Type': 'multipart/form-data'},
body: data
// body: JSON.stringify({
// url: this.state.url,
// name: this.state.name,
// description: this.state.description,
// filePath: this.state.file,
// })
})
.then(resp => console.log(resp));
}
Which then triggers this
AWS.config.update({
accessKeyId: process.env.AWSAccessKeyId,
secretAccessKey: process.env.AWSSecretKey,
region: 'eu-west-3'
});
const s3 = new AWS.S3();
app.post('/new-experience', (req, res) => {
const { url, name, description, filePath } = req.body;
console.log(filePath);
const params = {
Bucket: 'bucket-xxxx',
Body : fs.createReadStream(filePath),
Key : "folder/test.png"
};
s3.upload(params, function (err, data) {
//handle error
if (err) {
console.log("Error", err);
}
//success
if (data) {
console.log("Uploaded in:", data.Location);
}
});
db('mael-landrin').insert({
experienceurl: url,
experiencename: name,
experiencedescription: description
})
.into('experiences')
.then(resp => res.status(200).send('Experience added'))
.catch(err => res.status(400).send(err));
})
The console log in the 3rd line of the "/new-experience" route returns undefined, which I believe is why fs.createReadSream(filePath) gives undefined to the function, which is why I get the error.
The last console.log() in the React part gives me everything right, 3 strings and a file ( a .png file ).
Apple Website for Apple. https://www.apple.com/ File {name: "Entˆte Mozzarelline 25g x 8.jpg", lastModified: 1583766837712, lastModifiedDate: Mon Mar 09 2020 16:13:57 GMT+0100 (Central European Standard Time), webkitRelativePath: "", size: 454282, …}
Anyone knows where it could come from ?
The file object will not be present on your req.body. You need to install a third party module for file upload(of course there are "raw" ways to do it, but i don't know how and see no reason to do so):
npm i express-fileupload
In your express root file:
const fileUpload = require('express-fileupload');
app.use(fileUpload());
Then in your file upload route:
const { url, name, description} = req.body;
const fileObject = req.files.filePath;//"filePath" is misleading, but this is the name you chose.
const fileData = fileObject.data;
const params = {
Bucket: 'bucket-xxxx',
Body : fileData,
Key : "folder/test.png"
}
What you probably intended to do is to append file to data not file path. So do this
data.append('file', this.state.file);
You also need a middleware to parse multipart form data.
Multer works really well.
https://www.npmjs.com/package/multer

Resources