I am creating a MERN app in which I have created an api route /api/books
where user can POST a book’s details with it’s image, and the data will be stored in MongoDB.
I am using multer
and it stores the image after encoding in binary format in the database.
When I test it with postman, I works perfectly fine and the data is added in database and I receive response 200 status.
But I am facing issues in sending the data from react frontend to the api, I have created controlled forms and am storing the data value in states, then I call the function to POST data to api on form submit using axios, but I get error Multipart: Boundary not found
in my terminal, I think the file is not sent through axios correctly.
React form page:
// states
const [data, setData] = useState({
book_name: "",
book_author: "",
for_branch: "",
for_semester: "1",
});
const [file, setFile] = useState(null);
// to POST data onSubmit
const handleSubmit = async (e) => {
e.preventDefault();
setAddError("");
if (!file) {
setAddError("Add Book Image");
return;
} else if (file.size > 1000000) {
setAddError("Use Images less than 1MB");
return;
} else if (
!data.book_name ||
!data.book_author ||
!data.for_branch ||
!data.for_semester
) {
setAddError("Add All Details");
return;
}
await addBook(data, file);
toggleNotification();
};
// Form
<Form
onSubmit={handleSubmit}
className="form"
encType="multipart/form-data"
>
<Col className="image">
<Form.Group>
<Form.File
id="exampleFormControlFile1"
label="Upload Book Image"
onChange={onFileChange}
style={{ margin: "auto" }}
/>
{file ? (
<div>
<h5>File Name: {file.name}</h5>
<h5>Last Modified: {file.lastModifiedDate.toDateString()}</h5>
</div>
) : (
<h5>Choose before Pressing the Upload button</h5>
)}
<hr />
</Form.Group>
</Col>
<Col md={6} className="book-details">
{addError !== "" && (
<Alert variant="danger">
<FaInfoCircle /> {addError}
</Alert>
)}
<Form.Group controlId="exampleForm.ControlInput1">
<Form.Control
type="text"
className="custom-input"
placeholder="Enter book name"
name="book_name"
value={data.book_name}
onChange={handleChange}
/>
</Form.Group>
<Form.Group controlId="exampleForm.ControlInput2">
<Form.Control
type="text"
className="custom-input"
placeholder="Enter book author name"
name="book_author"
value={data.book_author}
onChange={handleChange}
/>
</Form.Group>
<Form.Group controlId="exampleForm.ControlInput3">
<Form.Control
type="text"
className="custom-input"
placeholder="Enter branches names"
name="for_branch"
value={data.for_branch}
onChange={handleChange}
/>
</Form.Group>
<Form.Group controlId="exampleForm.ControlSelect2">
<Form.Control
as="select"
className="custom-select"
name="for_semester"
onChange={handleChange}
>
<option default disabled>
select semester
</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
</Form.Control>
</Form.Group>
<Button variant="success" type="submit">
Add Book
</Button>
</Col>
</Form>
React code to POST data:
const addBook = async (formData, file) => {
dispatch({ type: SEND_LOADING });
formData = {
formData,
book_image: file,
};
console.log("data from form", formData);
const res = await axios.post(
"http://localhost:5000/api/books",
formData,
imageHeaderConfig()
);
const item = await res.data;
if (res.status !== 200) {
console.log("error geting sell books");
dispatch({ type: SENT_DETAILS, payload: null });
return;
}
console.log(item);
dispatch({ type: SENT_DETAILS, payload: item });
};
const imageHeaderConfig = () => {
const token = localStorage.getItem("token");
const config = {
headers: {
"Content-Type": "multipart/form-data",
Accept: "application/json",
type: "formData",
},
};
if (token) config.headers["x-auth-token"] = token;
console.log(config);
return config;
};
Api code:
const express = require("express"),
multer = require("multer"),
image = multer({
limits: {
fileSize: 1000000,
},
// storage: multer.memoryStorage(),
fileFilter(req, file, cb) {
if (!file.originalname.match(/.(jpg|png|JPG|PNG|JPEG|jpeg)$/))
return cb(new Error("Not a valid file format!"));
cb(undefined, true);
},
}),
router = express.Router();
router.post(
"/",
auth,
image.single("book_image"),
async (req, res) => {
console.log(req.user);
console.log(req.body);
console.log(req.file);
const newBook = new Book({
book_image: req.file.buffer,
added_by: {
id: req.user.id,
name: req.user.name,
},
book_name: req.body.book_name,
book_author: req.body.book_author,
for_branch: req.body.for_branch,
for_semester: req.body.for_semester,
sold: req.body.sold,
});
newBook.save().then((book) => {
res.json(book);
});
},
(err, req, res, next) => {
console.log(err.message);
res.status(400).json(err.message);
}
);
EDIT
As some of you suggested to use FormData object
So I changed the POST funtion to this, I still have the same error Multipart: Boundary not found
const addBook = async (data, file) => {
dispatch({ type: SEND_LOADING });
let formData = new FormData();
formData.append("book_name", data.book_name);
formData.append("book_author", data.book_author);
formData.append("for_semester", data.for_semester);
formData.append("for_branch", data.for_branch);
formData.append("book_image", file);
console.log("data from form", formData);
const res = await axios.post(
"http://localhost:5000/api/books",
formData,
imageHeaderConfig()
);
const item = await res.data;
if (res.status !== 200) {
console.log("error geting sell books");
dispatch({ type: SENT_DETAILS, payload: null });
return;
}
console.log(item);
dispatch({ type: SENT_DETAILS, payload: item });
};
Advertisement
Answer
You should be using the FormData
class to properly handle this, as demonstrated here.