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.