4

I'm trying to send a file/image from React to node.js server with multer. The problem is I can send an image only through Postman but when I'm trying the same in React I'm getting: TypeError: Cannot read property 'path' of undefined. I don't understand whether I should send a file/image in binary or use another format. I've already tried to use reader.readAsDataURL() and reader.readAsBinaryString() but it didn't work.


const multer = require("multer");

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "./uploads/");
  },
  filename: (req, file, cb) => {
    cb(null, new Date().toISOString() + file.originalname);
  }
});

const fileFilter = (req, file, cb) => {
  if (
    file.mimetype === "image/jpeg" ||
    file.mimetype === "image/png" ||
    file.mimetype === "image/jpg"
  ) {
    cb(null, true);
  }
  cb(null, false);
};

const upload = multer({
  storage: storage,
  limits: { fileSize: 5000000 },
  fileFilter: fileFilter
});

// Create a post
router.post(
  "/",
  upload.single("image"),
  passport.authenticate("jwt", { session: false }),

  (req, res) => {
    const { errors, isValid } = validationPostInput(req.body);
    if (!isValid) {
      return res.status(400).json(errors);
    }
    console.log(req.file);
    const newPost = new Post({
      text: req.body.text,
      theme: req.body.theme,
      name: req.body.name,
      avatar: req.body.avatar,
      image: req.file.path,
      user: req.user.id
    });

    newPost.save().then(post => res.json(post));
  }
);

  // CREATE A POST
export const createPost = (userInput, history) => dispatch => {
  const headers = {
    "Content-Type": "form-data"
  };
  axios
    .post("/post", userInput, headers)
    .then(res => history.push("/post/all"))
    .catch(err =>
      dispatch({
        type: GET_ERRORS,
        payload: err.response.data
      })
    );
};


   import React, { Component } from "react";
import PropTypes from "prop-types";
// import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { createPost } from "../../actions/postActions";
import "./style.css";

class CreatePost extends Component {
  state = {
    text: "",
    theme: "",
    image: "",
    errors: {}
  };

  componentWillReceiveProps(nextProps) {
    if (nextProps.errors) {
      this.setState({ errors: nextProps.errors });
    }
  }

  onSubmit = e => {
    e.preventDefault();

    const { user } = this.props.auth;

    const newPost = {
      text: this.state.text,
      theme: this.state.theme,
      image: this.state.image,
      name: user.username,
      avatar: user.avatar
    };

    this.props.createPost(newPost);
  };
  onChange = e => this.setState({ [e.target.name]: e.target.value });

  fileSelectHandler = e => {
    const param = e.target.files[0];
    let reader = new FileReader();
    reader.readAsDataURL(param);

    this.setState({
      image: reader.result
    });
    console.log(reader);
  };

  render() {
    const { text, theme, errors } = this.state;

    return (
      <section className="post">
        <form
          onSubmit={this.onSubmit}
          className="post__form"
          action="/post"
          method="POST"
          encType="multipart/form-data"
        >
          <div className="post__form--input">
            <label>Theme</label>
            <input
              type="text"
              name="theme"
              value={theme}
              onChange={this.onChange}
            />
            {errors && <small>{errors.theme}</small>}
          </div>
          <div className="post__form--input">
            <label>Text</label>
            <textarea
              type="text"
              name="text"
              value={text}
              onChange={this.onChange}
            />
            {errors && <small>{errors.text}</small>}
          </div>
          <div className="post__form--file">
            <label>Add Image</label>
            <input
              type="file"
              name="file"
              accept=".png, .jpg"
              onChange={this.fileSelectHandler}
            />
          </div>
          <button type="submit" className="button">
            Submit
          </button>
        </form>
      </section>
    );
  }
}

CreatePost.propTypes = {
  errors: PropTypes.object.isRequired
};

const mapStateToProps = state => ({
  errors: state.errors,
  auth: state.auth
});

export default connect(
  mapStateToProps,
  { createPost }
)(CreatePost);

3
  • hey can you generate a prototype here, it will be easy for debugging codesandbox.io/s/node Commented Jun 21, 2019 at 19:30
  • my server connects to mongoDB, I don't think I can do it Commented Jun 21, 2019 at 19:38
  • maybe you can mock your data Commented Jun 21, 2019 at 19:39

2 Answers 2

3

Finally, I found out how to solve that problem.

 onSubmit = e => {
    e.preventDefault();

    const { user } = this.props.auth;

    const form = new FormData();
    form.append("file", this.state.file);
    form.append("theme", this.state.theme);
    form.append("text", this.state.text);
    form.append("name", user.username);
    form.append("avatar", user.avatar);

    this.props.createPost(form);
  }

Sign up to request clarification or add additional context in comments.

Comments

0

You are sending the field name as file:

<input
    type="file"
    name="file"
    accept=".png, .jpg"
    onChange={this.fileSelectHandler}
/>

But you are setting the field name as "image" on multer:

upload.single("image")

You need to change it to:

upload.single("file")

The correct syntax is:

.single(fieldname)

Accept a single file with the name fieldname. The single file will be stored in req.file.

1 Comment

I changed but I got the same issue. Perhaps the problem in data I'm trying to send. Which format multer can red?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.