'Cannot get req.files using Multer, Express and React

im having troubles to get req.files, always appear undefined. Im using multer, i saw anothers posts where they try with upload.single("input name"). however didn't work too.

Controller

const db = require("../database/models");

const instruments = {
    get: async (req, res) =>{
        try{
            const instruments = await db.Instruments.findAll([{include: "imagenes"},{include: "Category"}])  

            return res.send(instruments)
        }catch(e){
            console.log(e)
        }
    },

    post: async (req, res) => {
        try{
            
            let data = req.body;
            console.log("req", req.body)
            
            let file = req.files;
            console.log("req.file", file)
            
            let instrumento = {
                nombre: String(data.nombre),
                fabricante: String(data.fabricante),
                precio: Number(data.precio),
                categoria_id: Number(data.categoria),
                descuento: String(data.descuento),
                precioDescuento: Number(data.precioDescuento),
                texto: String(data.texto),
                fecha: data.fecha,
                image: data.images
            }

            // let instrumentosCreated = await db.Instruments.create(instrumento);

            console.log("lleuge", instrumento)

            // let imagenes = [];
            
            // Array.from(file).forEach(img => {
            //     imagenes.push(img.images);
            // })
            // //console.log("imagenes", imagenes);
            // let imagenesCreated = await imagenes.forEach(img => {  
            //     db.Image.create({
            //         url_imagen: "/"+ data.nombre +"/" + img,
            //         instrumento_id: instrumentosCreated.id
            // })
        // });

            console.log("images", imagenesCreated);
            return res.send(instrumentosCreated);
            
        }catch(e){
            console.log(e);
        }
    }
}

module.exports = instruments

Backend Configuration

// ************ Require's ************

const express = require('express');
const path = require('path');
const app = express();
const cors = require('cors');
const bodyParser = require('body-parser');


// ************ Data Configuration ************

app.use(bodyParser.json())
app.use(express.urlencoded({ extended: false}));
app.use(express.json())

//CORS
app.use(cors());

// ************ Servidor ************

app.set("port", process.env.PORT || 5000);
app.listen(app.get("port"), () => console.log("Server start in http://localhost:"+app.get("port")));

// ************ Acceso Publico ************

app.use(express.static(path.resolve(__dirname, "../public"))); // Necesario para los archivos estáticos en el folder /public

// ************ API's ************

const apiInstrumentos = require("./routes/apiInstrumentos");

app.use(apiInstrumentos)


// ************ Router Define ************

const instrumentRouter = require("./routes/instrumentos.js");

app.use("/", instrumentRouter);

Route

const express = require('express');
const router = express.Router();
const path = require("path");
const fs = require("fs");
const instrumentos = require("../controllers/instrumentos");
const multer = require("multer");



// ************ Multer ************

const dest = multer.diskStorage({
    destination: function (req, file, cb) {
        let dir = path.resolve(__dirname,"../../public/uploads","instruments", String(req.body.nombre).trim().replace(/\s+/g, ''))
        if (!fs.existsSync(dir)){
            fs.mkdirSync(dir);
        }
        cb(null, dir)
    },
    filename: function (req, file, cb) {
        cb(null, file.fieldname + '-' + Date.now()+ path.extname(file.originalname))
    }
})

const upload = multer({storage:dest});

// ************ Rutas ************

router.get("/", instrumentos.get);
router.post("/guardar",[upload.any()], instrumentos.post); 


module.exports = router

Form React

import React,{Component} from 'react';
import Axios from 'axios';


class Form extends Component {
    
    state = {
            nombre: "",
            fabricante: "",
            precio: "",
            categoria: "",
            descuento: "",
            precioDescuento: "",
            texto: "",
            images: "",
            fecha: ""
    }

    changeHandler = e => {
        this.setState({ [e.target.name]: e.target.value})
    }
    
    submitHandler = e => {
        e.preventDefault();
        console.log(this.state)
        Axios
            .post('http://localhost:5000/guardar', this.state)
            .then(response => {
                console.log(response)
            })
            .catch(error => {
                console.log(error)
            })
    }


    render(){
    
    const {nombre,fabricante,precio,precioDescuento,texto,fecha,images} = this.state

    return(
        <main>
        <section className="titulo">
            <h2>Creación de Proyectos</h2>
        </section>
        <section className="formulario">
            <form onSubmit={this.submitHandler} method="POST" encType="multipart/form-data">
                <fieldset>
                    <label htmlFor="nombreInstrumento">Nombre de tu proyecto: </label>
                    <input type="text" name="nombre" value={nombre} onChange={this.changeHandler}/>
                </fieldset>
                <fieldset>
                    <label htmlFor="nombreFabricante">Nombre del fabricante:</label>
                    <input type="text" name="fabricante" value={fabricante} onChange={this.changeHandler} />
                </fieldset>
                <fieldset>
                    <h3>Seleccione el tipo de instrumento</h3>
                    <fieldset id="flexRadio">
                            <label htmlFor="categoria"></label>
                            <input type="radio" className="solicitar-radio" name="categoria" value={1} onChange={this.changeHandler}></input>
                            <p>Guitarra</p>
                            <label htmlFor="categoria"></label>
                            <input type="radio" className="solicitar-radio" name="categoria" value={2} onChange={this.changeHandler}></input>
                            <p>Bajo</p>
                            <label htmlFor="categoria"></label>
                            <input type="radio" className="solicitar-radio" name="categoria" value={3} onChange={this.changeHandler}></input>
                            <p>Teclados</p>
                            <label htmlFor="categoria"></label>
                            <input type="radio" className="solicitar-radio" name="categoria" value={4} onChange={this.changeHandler}></input>
                            <p>Bateria</p>
                            <label htmlFor="categoria"></label>
                            <input type="radio" className="solicitar-radio" name="categoria" value={5} onChange={this.changeHandler}></input>
                            <p>Microfono</p>
                    </fieldset>
                </fieldset>
                <fieldset>
                    <h3>Precio:</h3>
                    <label htmlFor="precioInstrumento"></label>
                    <input type="number" name="precio"  placeholder="EJ: 10.000"  value={precio} onChange={this.changeHandler}/>
                </fieldset>
                <fieldset>
                    <h3>¿Tiene descuento?</h3>
                    <fieldset id="flexRadio">
                            <label htmlFor="secundario-terminado"></label>
                            <input type="radio" className="solicitar-radio" name="descuento" value={1} onChange={this.changeHandler}></input>
                            <p>Si</p>
                            <label htmlFor="secundario-terminado"></label>
                            <input type="radio"  className="solicitar-radio" name="descuento" value={0} onChange={this.changeHandler}></input>
                            <p>No</p>
                    </fieldset>
                </fieldset>
                <fieldset>
                    <h3>Precio con descuento:</h3>
                    <label htmlFor="precioDescuento"></label>
                    <input type="number" name="precioDescuento"  placeholder="EJ: 10.000"  value={precioDescuento} onChange={this.changeHandler}/>
                </fieldset>
                <fieldset>
                    <h3>Información del Instrumento</h3>
                    <textarea rows="15" cols="30" placeholder="Información.." name="texto"  value={texto} onChange={this.changeHandler}></textarea>
                </fieldset>
                <fieldset>
                    <label htmlFor="fechaInstrumento">Establece la fecha de creación de tu instrumento</label>
                    <input type="date"  name="fecha"  value={fecha} onChange={this.changeHandler}/>
                </fieldset>
                <fieldset id="images">
                    <label htmlFor="images">Subí una o varias fotos</label>
                    <input type="file" name="images" id="fotosProyecto" value={images} onChange={this.changeHandler} multiple />
                </fieldset>
                <fieldset className="flex-botones">
                    <button type="submit"> Enviar</button>
                    <button type="reset"> Reiniciar</button>
                </fieldset>
            </form>
            </section>
    </main>
    )}
}

export default Form;

Console Info

req.body { nombre: 'Test', fabricante: 'Test', precio: '450000', categoria: '3', descuento: '0', precioDescuento: '0', texto: 'Test', images: 'C:\fakepath\marshall2.jpg', fecha: '2022-02-03' } req.file undefined

So i dont know what can be making trouble, i have the form encType="multipart/form-data", the multiple attribute for multiples images, and i think i had connect all good, from now thanks for the help :)



Solution 1:[1]

You are sending JSON object from frontend that is why req.files is empty. You need to send your data in formData object from frontend.

Change your submitHandler with this.

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

  const formData = new FormData();
  formData.append("nombre", this.state.nombre);
  formData.append("fabricante", this.state.fabricante);
  formData.append("precio", this.state.precio);
  formData.append("categoria", this.state.categoria);
  formData.append("descuento", this.state.descuento);
  formData.append("precioDescuento", this.state.precioDescuento);
  formData.append("texto", this.state.texto);
  formData.append("fecha", this.state.fecha);

  // images will be an array of file objects, you need to loop through array of images and append them to formData with same name
  this.state.images.forEach((img) => {
    formData.append("images", img);
  });

  Axios.post("http://localhost:5000/guardar", formData)
    .then((response) => {
      console.log(response);
    })
    .catch((error) => {
      console.log(error);
    });
};

If you don't know about FormData interface, please check this link. https://developer.mozilla.org/en-US/docs/Web/API/FormData

Also, you need to change your changeHandler because e.target.value does not contain file object. It just contains file path. You need to access files through e.target.files.

modify your chnageHandler with this code.

changeHandler = (e) => {
  if (e.target.name === "images") {
    this.setState({ [e.target.name]: e.target.files });
  } else {
    this.setState({ [e.target.name]: e.target.value });
  }
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1