admin管理员组文章数量:1122846
this is Omar,
Note: Too much talk? the sections below not all of them is a must to check if you are just trying to understand the problem, just see these ones:
- Front End (A really quick look at the code)
- Back End (You can check if you want, but it is just standard setup for multer)
- Postman (A quick look at request details, and dont look at its code)
- Network tab (Check it)
- What Happened in the Backend (understand the problem)
Front End
my problem is when sending FormData
using fetch in my front-end (react):
const formData = new FormData();
formData.append('file', values.image);
formData.append('path', 'image');
formData.append('model', 'item');
formData.append(
'data',
JSON.stringify({
menu: menu._id.toString(),
item: values.item,
})
);
const req = new Request(`/api/v1/upload`, {
method: 'POST',
body: formData,
});
The Request
constructor is the exact same as fetch, but it just skips doing response.json()
everytime, Request class is my own (I built it)
Back End
Backend code is using multer for multipart formData. Backend perfectly works fine with postman, so using postman to send the request works just fine, but using fetch as up does not. for the backend code, it is just simple, i use upload.single, and memoryStorage. Code:
const storage = multer.memoryStorage();
const upload = multer({ storage, fileFilter });
exports.processImage = (req, res, next) => {
if (!req.file) return next();
req.file.filename =
req.modelController.createFileName(req, req.file) + '.jpeg';
sharp(req.file.buffer)
.toFormat('jpeg')
.toFile(
path.join(
`public/image/upload/${req.body.model.toLowerCase()}`,
req.file.filename
)
);
next();
};
Again it works fine if i use postman
Postman
Postman request image Postman code is really similar to my code:
const formdata = new FormData();
formdata.append("model", "item");
formdata.append("path", "image");
formdata.append("data", "{\"menu\": \"673e442bae0dd4717f4af4c1\", \"item\": \"673f76230fb7a35ddfc2cd57\"}");
formdata.append("file", fileInput.files[0], "/C:/Users/LENOVO/Downloads/Matrix-Cola-Carbonated-Drink.jpg");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: formdata,
redirect: "follow"
};
fetch("http://127.0.0.1:5423/api/v1/upload/", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
What Happens in the Backend?
It receives the body as null Object, even using multer, and the file as undefined
Network Tab
network tabs shows the following Network tab, request payload Headers are going well, content type is multipart/form-data.
What did i try?
I did not try many things, but:
Content type
Setting the content type header is done automatically by form data instance
What is Values.image?
it is [Object File]
What form do i use
i use formik to handle forms. but am sure that the file is being saved at values.image
this is Omar,
Note: Too much talk? the sections below not all of them is a must to check if you are just trying to understand the problem, just see these ones:
- Front End (A really quick look at the code)
- Back End (You can check if you want, but it is just standard setup for multer)
- Postman (A quick look at request details, and dont look at its code)
- Network tab (Check it)
- What Happened in the Backend (understand the problem)
Front End
my problem is when sending FormData
using fetch in my front-end (react):
const formData = new FormData();
formData.append('file', values.image);
formData.append('path', 'image');
formData.append('model', 'item');
formData.append(
'data',
JSON.stringify({
menu: menu._id.toString(),
item: values.item,
})
);
const req = new Request(`/api/v1/upload`, {
method: 'POST',
body: formData,
});
The Request
constructor is the exact same as fetch, but it just skips doing response.json()
everytime, Request class is my own (I built it)
Back End
Backend code is using multer for multipart formData. Backend perfectly works fine with postman, so using postman to send the request works just fine, but using fetch as up does not. for the backend code, it is just simple, i use upload.single, and memoryStorage. Code:
const storage = multer.memoryStorage();
const upload = multer({ storage, fileFilter });
exports.processImage = (req, res, next) => {
if (!req.file) return next();
req.file.filename =
req.modelController.createFileName(req, req.file) + '.jpeg';
sharp(req.file.buffer)
.toFormat('jpeg')
.toFile(
path.join(
`public/image/upload/${req.body.model.toLowerCase()}`,
req.file.filename
)
);
next();
};
Again it works fine if i use postman
Postman
Postman request image Postman code is really similar to my code:
const formdata = new FormData();
formdata.append("model", "item");
formdata.append("path", "image");
formdata.append("data", "{\"menu\": \"673e442bae0dd4717f4af4c1\", \"item\": \"673f76230fb7a35ddfc2cd57\"}");
formdata.append("file", fileInput.files[0], "/C:/Users/LENOVO/Downloads/Matrix-Cola-Carbonated-Drink.jpg");
const requestOptions = {
method: "POST",
headers: myHeaders,
body: formdata,
redirect: "follow"
};
fetch("http://127.0.0.1:5423/api/v1/upload/", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
What Happens in the Backend?
It receives the body as null Object, even using multer, and the file as undefined
Network Tab
network tabs shows the following Network tab, request payload Headers are going well, content type is multipart/form-data.
What did i try?
I did not try many things, but:
Content type
Setting the content type header is done automatically by form data instance
What is Values.image?
it is [Object File]
What form do i use
i use formik to handle forms. but am sure that the file is being saved at values.image
Share Improve this question asked Nov 22, 2024 at 21:04 The Khateeb DevThe Khateeb Dev 241 bronze badge2 Answers
Reset to default 0Your frontend code using Request is closely related to fetch because the Request object is designed to be used with the Fetch API. The Request class itself doesn’t perform the actual network operation—it simply creates a Request object that you typically pass to fetch.
The problem arises because your backend isn't correctly receiving the multipart FormData payload when using fetch
.
The issue is related to the Fetch API because your custom Request class likely wraps it. The root cause is the handling of the Content-Type header and how FormData is passed. You can resolve this by ensuring the Request class or fetch is used correctly or by switching to a library like axios for consistency.
axios.post
inspects the FormData and sets the Content-Type header with the appropriate boundary (a required part of the multipart/form-data format).
This ensures the server correctly interprets the form data.
Demo
server.js
const express = require('express');
const cors = require('cors');
const multer = require('multer');
const sharp = require('sharp');
const path = require('path');
const fs = require('fs');
// Initialize the Express app
const app = express();
// Middleware for JSON parsing and CORS
app.use(express.json());
app.use(cors());
// Configure multer storage in memory
const storage = multer.memoryStorage();
const upload = multer({ storage });
// Ensure upload directories exist
const ensureUploadDirExists = (dirPath) => {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
};
// POST route to handle file uploads
app.post('/api/v1/upload', upload.single('file'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded.' });
}
const { path: uploadPath, model, data } = req.body;
// Parse and log the additional data
const parsedData = JSON.parse(data);
console.log('Parsed data:', parsedData);
// Ensure the directory exists
const uploadDir = path.join(__dirname, 'public', uploadPath, model.toLowerCase());
ensureUploadDirExists(uploadDir);
// Create a unique filename
const filename = `${Date.now()}-${model.toLowerCase()}.jpeg`;
// Process and save the image
await sharp(req.file.buffer)
.toFormat('jpeg')
.toFile(path.join(uploadDir, filename));
// Respond with the file path and other details
res.json({
success: true,
message: 'File uploaded successfully.',
filePath: `/public/${uploadPath}/${model.toLowerCase()}/${filename}`,
data: parsedData,
});
} catch (error) {
console.error('Error processing upload:', error);
res.status(500).json({ success: false, error: 'Failed to process upload.' });
}
});
// Server listening
const PORT = process.env.PORT || 5423;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Dependency Install for server
npm install express cors multer sharp
In frontend
App.js
import React, { useState } from 'react';
import axios from 'axios';
const App = () => {
const menu = { _id: '12345' };
const [imageFile, setImageFile] = useState(null); // File input
const [itemName, setItemName] = useState(''); // Item name input
const [responseMessage, setResponseMessage] = useState('');
// Handle file input change
const handleFileChange = (event) => {
setImageFile(event.target.files[0]);
};
const handleSubmit = async (event) => {
event.preventDefault();
if (!imageFile) {
setResponseMessage('Please select an image to upload.');
return;
}
const formData = new FormData();
formData.append('file', imageFile);
formData.append('path', 'image');
formData.append('model', 'item');
formData.append(
'data',
JSON.stringify({
menu: menu._id.toString(),
item: itemName,
})
);
try {
const response = await axios.post('http://localhost:5423/api/v1/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
console.log(response.data);
setResponseMessage(JSON.stringify(response.data, null, 2)); // Format JSON response
} catch (error) {
console.error('Error details:', error.response?.data || error.message);
setResponseMessage('Error: Could not get a response.');
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<label>
Upload Image:
<input type="file" onChange={handleFileChange} />
</label>
</div>
<div>
<label>
Item Name:
<input
type="text"
value={itemName}
onChange={(e) => setItemName(e.target.value)}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
{responseMessage && (
<div>
<h3>Server Response:</h3>
<pre>{responseMessage}</pre>
</div>
)}
</div>
);
};
export default App;
Dependency Install
npm install axios
Result
node server.js
npm start
Postman
Updating step by step
0. Checking npx version and others
npx --version
npm --version
node --version
1. Create Frontend
npx create-react-app frontend
result
checking by tree
cd frontend
tree /F
2. Install axios
npm install axios
result
the package.json
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
3. Replace App.js with my code
4. Run server.js
npm install express cors multer sharp
node server.js
5. Run frontend
At frontend
directory
npm start
6. Got response from server
Update 2 using Request()
import React, { useState } from 'react';
const App = () => {
const menu = { _id: '12345' };
const [imageFile, setImageFile] = useState(null); // File input
const [itemName, setItemName] = useState(''); // Item name input
const [responseMessage, setResponseMessage] = useState('');
// Handle file input change
const handleFileChange = (event) => {
setImageFile(event.target.files[0]);
};
const handleSubmit = async (event) => {
event.preventDefault();
if (!imageFile) {
setResponseMessage('Please select an image to upload.');
return;
}
const formData = new FormData();
formData.append('file', imageFile);
formData.append('path', 'image');
formData.append('model', 'item');
formData.append(
'data',
JSON.stringify({
menu: menu._id.toString(),
item: itemName,
})
);
try {
// Create a new Request object
const req = new Request('http://localhost:5423/api/v1/upload', {
method: 'POST',
body: formData,
});
// Use fetch with the Request object
const response = await fetch(req);
// Parse and handle the response
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const responseData = await response.json();
console.log(responseData);
setResponseMessage(JSON.stringify(responseData, null, 2)); // Format JSON response
} catch (error) {
console.error('Error details:', error.message);
setResponseMessage('Error: Could not get a response.');
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<label>
Upload Image:
<input type="file" onChange={handleFileChange} />
</label>
</div>
<div>
<label>
Item Name:
<input
type="text"
value={itemName}
onChange={(e) => setItemName(e.target.value)}
/>
</label>
</div>
<button type="submit">Submit</button>
</form>
{responseMessage && (
<div>
<h3>Server Response:</h3>
<pre>{responseMessage}</pre>
</div>
)}
</div>
);
};
export default App;
Result
: Same result using "Request()"
I've solved the CASE!!
For anyone who is getting this problem, please follow these EASY steps:
Steps:
-Check if you have the same thing as i do:
My problem wasnt that backend does not receive the formData
, the problem was that fileFilter
does not receive formData
, because multer
does not offer req.body
nor req.file
with it, how to check if that is your problem? go to fileFilter
function, and log the body
, and then go to processImage
function and log the body, if it logs in processImage
, but not in fileFilter
, then you have my problem, if it doesnt log for both, I dont think that my solution will work for you
-How to solve this problem?
go to your front-end, appending data like this:
formData.append('file', values.file);
formData.append('textContent', 'textContent');
will not work, you must make the append function for file
field at the end, as below:
formData.append('textContent', 'textContent');
formData.append('file', values.file);
And your problem have been solved. Congrats!
Why does this problem happen?
simply because postman sends the data of the form as one piece, and the file field is at the end, while front end, you made file field first, so while multer
process the file field, it runs fileFilter
, so fileFilter
does not gets the body, nor the file, while when sending body first then file, the body gets processed, so fileFilter
Thanks for everyone.
By the way this case is answered before, but people who gets into this problem does not know that this is same problem as this.
本文标签:
版权声明:本文标题:reactjs - Sending FormData from my front end to backend does not work (req.body in backend is null) - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736300871a1930975.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论