2

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 });
  };
sachuverma
  • 559
  • 6
  • 27
  • Form data is not just an Object. You need to use `var formData = new FormData(); formData.append('book_image', file);` https://developer.mozilla.org/en-US/docs/Web/API/FormData/append – Molda Mar 11 '21 at 11:55

1 Answers1

2

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

Chris Tanner
  • 90
  • 1
  • 6
  • I have appended all the key,value pairs from my data to FormData object, and sent that using axios, but still the error is same. I have also tried removing the headers, or only using content-type multipart/form-data only but still error is present. I have updated post to show what changes I did. – sachuverma Mar 11 '21 at 12:18
  • If it's working in Postman, I would start by replicating that in Axios (headers, params, etc). Once you have it working in Axios, you can start removing things one by one to see what is causing the issue. Have you seen [this question](https://stackoverflow.com/questions/49579640/how-to-send-data-correct-axios-error-multipart-boundary-not-found/49580158)? – Chris Tanner Mar 11 '21 at 12:22