Search code examples
reactjsexpressmultermern

Image upload in mern stack using multer not working


I'm trying to upload image in MongoDB using multer and react but I'm unable to post it. I have three inputs in by form i.e title, content and image. If I try to post title and content only it is successfully being posted. I have also added "proxy": "http://localhost:8000", in frontend package.json file

Here is my form

function PostCreate() {
  const [title, setTile] = useState("");
  const [content, setContent] = useState("");
  const [image, setImage] = useState({});
  const dispatch = useDispatch();
  const post = useSelector((state) => state.postReducer);

  const fileOnChange = (e) => {
    setImage(e.target.files[0]);
  };
  const submitPost = (e) => {
    e.preventDefault();
    const formData = new FormData();
    formData.append("image", image);
    dispatch(createPost(title, content, image));
  };

  return (
    <div className="postCreate">
      <h3 className="postCreate__heading">Create New Post</h3>
      <div className="formBody">
        <form onSubmit={submitPost}>
          <div className="formInputs">
            <label className="label">Title</label>
            <input
              className="titleInput"
              type="text"
              value={title}
              onChange={(e) => setTile(e.target.value)}
              placeholder="Enter the Title of the Post"
            />
          </div>
          <div className="formInputs">
            <input type="file" onChange={fileOnChange} />
          </div>
          <div className="formInputs">
            <label className="label">Content</label>
            <textarea
              className="titleInput"
              type="text"
              style={{
                width: "1500px",
                height: "500px",
                color: "black",
                outline: "none",
                border: "none",
                borderRadius: "6px",
              }}
              value={content}
              onChange={(e) => setContent(e.target.value)}
            />
          </div>
          <div className="button">
            <Button type="submit" variant="contained" color="primary">
              Post
            </Button>
          </div>

          {/* <button type="submit">Post</button> */}
        </form>

Here is my action

export const createPost = (title, content, image) => async (dispatch) => {
  try {
    dispatch({ type: POST_POST_LOADING });
    const config = { headers: { "Content-Type": "application/json" } };
    const { data } = await axios.post(
      "/api/newpost",
      { title, content },
      config
    );
    dispatch({
      type: POST_POST_SUCCESS,
      payload: data,
    });
  } catch (error) {
    dispatch({
      type: POST_POST_FAIL,
      payload: error,
    });
  }
};

Here is my postController

const createPost = async (req, res) => {
  const { title, content, writer, comment, image } = req.body;
  const fileType = req.file.mimetype.split("/")[1];
  const newFileName = req.file.filename + "." + fileType;
  fs.rename(
    `uploads/images/${req.file.filename}`,
    `uploads/images/${newFileName}`,
    (req, res) => {
      console.log("Renamed");
    }
  );
  // const imageUrl = req.file.filename;
  const newPost = await Post.create({
    title,
    content,
    writer,
    comment,
    image,
    // imageUrl,
  });
  if (newPost) {
    res.send("Post created");
    console.log("Post created");
  } else {
    res.status(201).send("Post not created");
    console.log("Post not created");
  }
};

Here is my routes

router.post("/newpost", upload.single("image"), createPost);

enter image description here


Solution

  • You're creating a form, which is a good start, but not sending it with axios.

    To send a file from frontend to backend, you need to construct a form using FormData API and append the file to it. You can also append additional data to it.

    Here is how I would change your code to work. In your form file:

    const formData = new FormData();
    formData.append('image', image);
    formData.append('title', title);
    formData.append('content', content);
    dispatch(createPost(formData));
    

    Then change your action to:

    export const createPost = (formData) => async (dispatch) => {
      try {
        dispatch({ type: POST_POST_LOADING });
        const config = { headers: { "Content-Type": "multipart/form-data" } };
        const { data } = await axios.post(
          "/api/newpost",
          formData,
          config
        );
        dispatch({
          type: POST_POST_SUCCESS,
          payload: data,
        });
      } catch (error) {
        dispatch({
          type: POST_POST_FAIL,
          payload: error,
        });
      }
    };