Trying to use multer but file.path returned undefined

I’m really trying to have one picture per movie but is currently not working, I’m using multer to achieve this but is the first time I’m doing it and path is undefined for some reason.

const { Movie, validate } = require("../models/movie");
const { Genre } = require("../models/genre");
const auth = require("../middleware/auth");
const admin = require("../middleware/admin");
const validateObjectId = require("../middleware/validateObjectId");
const moment = require("moment");
const multer = require ("multer");
const express = require("express");
const router = express.Router();

const storage = multer.diskStorage({
  destination: function(req, file, cb) {
    cb(null, 'img/');
  },
  filename: function(req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

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

const upload = multer({ 
    storage: storage, 
    limits: { fileSize: 1024*1024*5 },
    fileFilter: fileFilter 
});

router.get("/", async (req, res) => {
  const movies = await Movie.find()
    .select("-__v")
    .sort("name");
  res.send(movies);
});

router.post("/", upload.single('movieImg'), [auth], async (req, res) => {
  const { error } = validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const genre = await Genre.findById(req.body.genreId);
  if (!genre) return res.status(400).send("Invalid genre.");

  const movie = new Movie({
    title: req.body.title,
    genre: {
      _id: genre._id,
      name: genre.name
    },
    numberInStock: req.body.numberInStock,
    dailyRentalRate: req.body.dailyRentalRate,
    movieImg: req.file.path, //where the error is comming from
    publishDate: moment().toJSON()
  });
  await movie.save();

  res.send(movie);
});

module.exports = router;

is req.file defined? or is it specifically req.file.path?
Can you show me what you get for req.file?

also, what are you sending to the endpoint?
Is the field for the file named ‘movieImg’ ?

I’m having a Cannot GET / in localhost:3900 but the images do upload I founded an img/ folder with all the images I uploaded, but why it says undefined then?, here is my movie.js router, thanks for your answer.

const { Movie, validate } = require("../models/movie");
const { Genre } = require("../models/genre");
const auth = require("../middleware/auth");
const admin = require("../middleware/admin");
const validateObjectId = require("../middleware/validateObjectId");
const moment = require("moment");
const mongoose = require("mongoose");
const multer = require ("multer");
const express = require("express");
const router = express.Router();

const storage = multer.diskStorage({
  destination: function(req, file, cb) {
    cb(null, 'img/');
  },
  filename: function(req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

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

const upload = multer({ 
    storage: storage, 
    limits: { fileSize: 1024*1024*5 },
    fileFilter: fileFilter 
});

router.get("/", async (req, res) => {
  const movies = await Movie.find()
    .select("-__v")
    .sort("name");
  res.send(movies);
});

router.post("/", upload.single('movieImg'), [auth], async (req, res) => {
  const { error } = validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const genre = await Genre.findById(req.body.genreId);
  if (!genre) return res.status(400).send("Invalid genre.");

  console.log(req.file);

  const movie = new Movie({
    title: req.body.title,
    genre: {
      _id: genre._id,
      name: genre.name
    },
    numberInStock: req.body.numberInStock,
    dailyRentalRate: req.body.dailyRentalRate,
    movieImg: req.file.path, //where the error is comming from
    publishDate: moment().toJSON()
  });
  await movie.save();

  res.send(movie);
});

router.put("/:id", upload.single('movieImg'), [auth], async (req, res) => {
  const { error } = validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);

  const genre = await Genre.findById(req.body.genreId);
  if (!genre) return res.status(400).send("Invalid genre.");

  const movie = await Movie.findByIdAndUpdate(
    req.params.id,
    {
      title: req.body.title,
      genre: {
        _id: genre._id,
        name: genre.name
      },
      numberInStock: req.body.numberInStock,
      dailyRentalRate: req.body.dailyRentalRate,
      movieImg: req.file.path
    },
    { new: true }
  );

  if (!movie)
    return res.status(404).send("The movie with the given ID was not found.");

  res.send(movie);
});

router.delete("/:id", [auth, admin], async (req, res) => {
  const movie = await Movie.findByIdAndRemove(req.params.id);

  if (!movie)
    return res.status(404).send("The movie with the given ID was not found.");

  res.send(movie);
});

router.get("/:id", validateObjectId, async (req, res) => {
  const movie = await Movie.findById(req.params.id).select("-__v");

  if (!movie)
    return res.status(404).send("The movie with the given ID was not found.");

  res.send(movie);
});

module.exports = router;

in my Post route I have a 404 error here is my frontend implementation with React I know is a lot of code but please check out.

import React from "react";
import Joi from "joi-browser";
import Form from "./common/form";
import { getMovie, saveMovie } from "../services/movieService";
import { getGenres } from "../services/genreService";
import axios from 'axios';
import { ToastContainer, toast } from 'react-toastify';
import { Progress } from 'reactstrap';
import { apiUrl } from '../config.json';
import 'react-toastify/dist/ReactToastify.css';

const apiEndPoint = apiUrl + '/img';

class MovieForm extends Form {
    state = {  
        data: {
            title: "",
            genreId: "",
            numberInStock: "",
            dailyRentalRate: "",
            movieImg: { movieImg: null, loaded: 0}
          },
          genres: [],
          errors: {}
    }

    schema = {
        _id: Joi.string(),
        title: Joi.string()
          .required()
          .label("Title"),
        genreId: Joi.string()
          .required()
          .label("Genre"),
        numberInStock: Joi.number()
          .required()
          .min(0)
          .max(100)
          .label("Number in Stock"),
        dailyRentalRate: Joi.number()
          .required()
          .min(0)
          .max(10)
          .label("Daily Rental Rate"),
        movieImg: Joi.string().allow(null).optional()
    }

    async populateGenres() {
      const { data: genres } = await getGenres();
        this.setState({ genres });
    }

    async populateMovie() {
      try {
        const movieId = this.props.match.params.id;
        if (movieId === "new") return;

        const { data: movie } = await getMovie(movieId);
        this.setState({ data: this.mapToViewModel(movie) });
      } catch(ex) {
       if (ex.response && ex.response.status === 404) 
        this.props.history.replace("/not-found");
      }
    }

    async componentDidMount() {
        await this.populateGenres();    
        await this.populateMovie();
    }
    
      mapToViewModel(movie) {
        return {
          _id: movie._id,
          title: movie.title,
          genreId: movie.genre._id,
          numberInStock: movie.numberInStock,
          dailyRentalRate: movie.dailyRentalRate,
          movieImg: movie.movieImg
        };
    }

    onChangeHandlerImage = async event => {
      var image = event.target.files[0];
    
      this.setState({
        movieImg: {
          movieImg: image, loaded: 0
        }
      });
    }
    
    doSubmitImage = async () => {
       const data = new FormData();
       data.append('file', this.state.movieImg);
     
       axios.post(apiEndPoint, data, {
         onUploadProgress: ProgressEvent => {
             this.setState({
               movieImg: { loaded: (ProgressEvent.loaded / ProgressEvent.total*100) }
             })
           },
         })
         .then(res => { toast.success('upload success') })
         .catch(err => { toast.error('upload fail') });
    }

    doSubmit = async () => {
        await saveMovie(this.state.data);
       
        this.props.history.push("/movies");
    }

    render() { 
        return ( 
            <div className = "container">
              <div className = "height"></div>
              <div className="form-group">
                <ToastContainer />
              </div>
              <div className = "card card-login card-hidden">
              <div className = "card-header card-header-primary text-center">
                <h4>Movie Form</h4>
              </div>
              <form accept="image/png,image/jpg,image/jpeg" encType='multipart/form-data' onSubmit={this.handleSubmit} className="group-flex">
                <div className = "card-body text-center p-4">
                {this.renderInput("title", "Title")}
                {this.renderSelect("genreId", "Genre", this.state.genres)}
                {this.renderInput("numberInStock", "Number in Stock", "number")}
                {this.renderInput("dailyRentalRate", "Rate")}
                <label htmlFor="file" className="btn btn-primary">Choose your file</label>
                <div className="form-group files">
                  <input type="file" name="file" id="file" className="input-file" 
                  multiple onChange={this.onChangeHandlerImage} />
                </div>
                <button type="button" className="btn btn-primary"
                onClick={this.doSubmitImage} >Upload</button>
                <div className="form-group my-5">
                <Progress max="100" color="primary" value={this.state.loaded} >{Math.round(this.state.loaded,2) }%</Progress>
                </div>
                {this.renderButton("Save")}
                </div>
              </form>
              </div>
            </div>
         );
    }
}
 
export default MovieForm;

I don’t have the undefined path error anymore I don’t know why but it’s gone but now I have a 404 error

estoy teniendo un Cannot GET / del lado del backend :frowning:

I solved the Cannot Get but the images upload problem I think it’s a frontend problem.

@Marie000 now I created a folder named “public” on my backed and with postman is working, is saving the images but how do I get that images to the frontend I mean how do I display an image I already have saved?

@Marie000 I think my endpoint is wrong I have this for the post http://localhost:3900/api/movies/ in my react app and it says error 400 bad request.

doSubmitImage = async () => {
 const data = new FormData();
 data.append('file', this.state.movieImg);
 axios.post(apiEndPoint, data, { onUploadProgress: ProgressEvent => { this.setState({ movieImg: { loaded: (ProgressEvent.loaded / ProgressEvent.total*100) } }) }, }) .then(res => { toast.success('upload success') }) .catch(err => { toast.error('upload fail') }); }

Ok, I am not sure I follow everything that is going on, but here you are sending data to upload endpoint. This probably looks like {movieImg: {movieImg: , loaded: } }, right?
so data.movieImg is not the actual image. That would be data.movieImg.movieImg ? That might be where multer gets confused?

I’m not sure why your image would still upload then.
I haven’t used multer with diskStorage myself (I upload to a google storage bucket), so I am not 100% sure how the diskStorage works.

Ok thanks :slight_smile: for the help I think multer is too complicated maybe I should try this tutorial https://www.youtube.com/watch?v=b6Oe2puTdMQ with express-fileupload he said it’s simpler, I’ll give it a shot.