Cannot POST image to my API Route from React.js

Tags: , , , ,



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.

enter image description here

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.

enter image description here

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 });
  };

Answer

You should be using the FormData class to properly handle this, as demonstrated here.



Source: stackoverflow