24

I've looked at a lot of answer for this same question, but I haven't found a working solution yet. I am trying to make a web app that you can upload files to using express and multer, and I am having a problem that no files are being uploaded and req.file is always undefined.

My code below

'use strict';

var express = require('express');
var path    = require('path');
var multer  = require('multer')
var upload  = multer({ dest: 'uploads/' })

var app = express();
require('dotenv').load();

app.use(express.static(path.join(__dirname, 'main')));

app.post('/upload', upload.single('upl'), function (req, res, next) {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
  console.log(req.file);
  res.status(204).end();
})

var port = process.env.PORT || 8080;
app.listen(port,  function () {
    console.log('Node.js listening on port ' + port + '...');
});

The form

    <form class="uploadForm" action="/upload" method="post" enctype="multipart/formdata">
        <label class="control-label">Select File</label>
        <input name="upl" id="input-1" type="file" class="file">
        <input type="submit" value="submit" />
    </form>

Help very much appreciated, this is driving me crazy.

mlamp
  • 773
  • 2
  • 9
  • 21

11 Answers11

42

In case of postman, try following:

  1. Close the postman tab for the API
  2. Open a new tab again
  3. Rebuild the API request and then send.

This may fix the problem. Every time you restart the server you need to do above steps for calling the API again. The reason being multer sends back some cookies called connect.sid to the client which it may require in further communication. Using old cookies will not upload the file.

Dmitry Shvedov
  • 3,169
  • 4
  • 39
  • 51
Mohit Mittal
  • 643
  • 6
  • 13
28

Your enctype is slightly incorrect, it should be multipart/form-data instead of multipart/formdata.

mscdex
  • 104,356
  • 15
  • 192
  • 153
  • Did you get this issue? https://stackoverflow.com/questions/49544671/expressjs-to-upload-an-image-using-multer-saves-an-image-in-a-weird-format – CSG0811 Mar 29 '18 at 05:19
  • I struggled with multipart/form-data, but for a different reason...Multer only accepts Content-Type that is multipart/form-data, but my client had that right and it still didn't work... In my case Multer couldn't parse the multipart form data / boundaries in the request body set by the client (a Java server). My solution was use wireshark and inspect the request body. I compared the results to a request created by Postman...I needed to tweak some CRLFs and the boundary being used wasn't accepted by Multer for whatever reason. I used the boundary generated by postman and then it worked... – Christian Bangert Apr 02 '21 at 16:06
1

I put MY (there are many I imagine and surely better) solution to help many people like me because I have searched during 1 entire day ;-(


//JS file on node side

var express = require('express');
var fileUpload = require('express-fileupload');
var fs = require("fs");
var app = express();
console.log('étape 0');
app.use(express.static('mesStatic'));
app.use(fileUpload());
console.log('étape 1');
app.get('/indexFileUpload.htm', function (req, res) {
   res.sendFile( __dirname + "/" + "indexFileUpload.htm" );
})
console.log('étape 2');
app.post('/file_upload', function (req, res) {

   console.log('étape 3');
   console.log('req.files:' , req.files);
   if (!req.files) {
       res.send('No files to upload.');
       return;
   }

   console.log('req.files.file.data:' , req.files.file.data);
   var bufDataFile = new Buffer(req.files.file.data, "utf-8");
   console.log('étape 3.1');
   console.log('__dirname : ' + __dirname);
   fs.writeFile(__dirname + '/file_upload/output.txt', bufDataFile,  function(err) {
      if (err) {
         return console.error(err);
      }
      else {
         console.log("Data written successfully !");
      }      
      console.log('étape 4');
      res.end('Fin OK !!!');  
   })
})
var server = app.listen(8081, function () {
   var host = server.address().address
   var port = server.address().port

   console.log("Example app listening at http://%s:%s", host, port);
})
1

Yes, your enctype is wrong and that is the only problem. Make sure that you correct your enctype otherwise you are likely to get undefined in req.file or req.files.

Piyush Patel
  • 1,646
  • 1
  • 14
  • 26
1

Hello Guys I have also wasted 24 hours and was getting same error i.e req.file is undefined. Now you can refer below code to check your solution. This is single file demo code for uploading file using multer in Node and postman. In POSTMAN Select POST method and select form-data and Key name should be 'profile'.

var express = require('express');
const app = express();
const port = 3000;
var multer = require('multer');
var upload = multer({dest:'uploads/'});
var storage = multer.diskStorage({
    destination: function(req, file, cb) {
        cb(null, './uploads');
     },
    filename: function (req, file, cb) {
        cb(null , file.originalname);
    }
});
var upload = multer({ storage: storage })

app.post('/single', upload.single('profile'), (req, res, error) => {
    try {
      console.log(req.file);
      res.send(req.file);
    }catch(err) {
      res.send(400);
    }
  });
app.get('/', (req, res) => {
    res.send('hello Guys');
});
app.listen(port, () => {
    console.log('listening to the port: ' + port);
});
Bharat Jha
  • 11
  • 2
1

If you are using error handling as mentioned in the document, then req.file will be undefined.

An alternative way to use both error handling and to check if the file exists is to make use of express error handling:

index.js

const express = require("express");
const bodyParser = require("body-parser");
const upload = require("./upload");
const multer = require("multer");

const app = express();

app.use(bodyParser.urlencoded({ extended: true }));

app.get("/", function (req, res) {
  res.send("Hello World");
});

app.post("/upload_file", upload.single("file"), function (req, res) {
  if (!req.file) {
    throw Error("FILE_MISSING");
  } else {
    res.send("success");
  }
});

//Express Error Handling
app.use(function (err, req, res, next) {
  if (err instanceof multer.MulterError) {
    res.statusCode = 400;
    res.send(err.code);
  } else if (err) {
    if (err.message === "FILE_MISSING") {
      res.statusCode = 400;
      res.send("FILE_MISSING");
    } else {
      res.statusCode = 500;
      res.send("GENERIC_ERROR");
    }
  }
});

const server = app.listen(8081, function () {
  const port = server.address().port;

  console.log("App started at http://localhost:%s", port);
});

upload.js

const multer = require("multer");
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "./uploads");
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + "_" + file.originalname);
  },
});

const upload = multer({
  storage: storage,
  //   limits: { fileSize: 10 },
});

module.exports = upload;
Abhishek E H
  • 479
  • 2
  • 7
  • 14
0

HTML File,

<form class="uploadForm" action="/upload" method="post" enctype="multipart/form-data">
    <label class="control-label">Select File</label>
    <input name="upl" id="input-1" type="file" class="file">
    <input type="submit" value="submit" />
</form>

app.js

var express=require("express");
var multer=require("multer");
var app=express();
var upload=multer({dest:"uploads/"});
app.post("/upload",upload.single("upl"),function(req,res){
console.log("Uploaded Successfull with filename : "+req.upl.filename);
});
Yash Bele
  • 676
  • 7
  • 7
0

For us this was because we were using express-http-proxy to proxy the call before multer, and we needed to use the parseReqBody: false option to properly send over the file.

ie.

app.post('file/upload', proxy(process.env.API_URL, {
  parseReqBody: false,
}))
zpr
  • 2,886
  • 1
  • 18
  • 21
0

Use:

formdata.append('file',document.getElementById("input-file").files[0]);

Instead of:

formdata.append('file',document.getElementById("input-file").files);

This is where I have done the mistake.

Yousha Aleayoub
  • 4,532
  • 4
  • 53
  • 64
0

1- Add the below lines in your server.js or index.js root file

 app.use(express.json());
 app.use(express.urlencoded({
  extended: true,
  })
 );

2- Create a folder name middleware and inside it create a file name upload.js.

3- Place the following code in upload.js file

 const multer = require("multer");

 const storage = multer.diskStorage({
 destination: function (req, file, cb) {
 cb(null, "public/");
},

 filename: function (req, file, cb) {
 const imgName = file.originalname;
 cb(null, imgName );
 },
});

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

module.exports = upload;

4- Add this middleware in any route. For example:

const upload = require("../middlewares/fileUpload");

router.route("/send").post(upload.single("profile"), 
((req,res) => {
 *your controller logic here*
});


**Note: Here profile is the name of the key of image or file that you are sending**
Muhammad Haidar
  • 1,541
  • 16
  • 17
0

having a similar issue with req.file being undefined. It looks like the image file is available up until the hook useFormSubmit wraps it in the formData object and sends the formData request to the router/controller.

However even just sending the image using the body, nothing comes through from the hook to the controller. I've included images of the console logs for the status of the data from the component to the controller below, would appreciate any guidance on this:

Things I've tried:

-Sending the form in incognito to avoid cookies or caching

-Sending reviewImage data in the body itself without wrapping it in formData.append

-including the multer middleWare directly in the router and controller

-pulling the image from req.body instead of req.file

CreateReview.tsx

//hooks 
    import {useFormSubmit} from '../../Hooks/useFormSubmit'

//dependencies
import { useForm, SubmitHandler } from "react-hook-form";

export const CreateReview = () => {

const { register, handleSubmit, formState: { errors } } = useForm<ReviewInputs>();
const {createReview, error, isLoading} = useFormSubmit()

const onSubmit: SubmitHandler<ReviewInputs> = async (data) => {
    const userID = user
    const title: string = data.title
    const review: string = data.review
    const artist: string = data.artist
    const author: string = data.author
    const authorBand: string = data.authorBand
    const banner: string = data.banner
    const reviewImage: any = data.reviewImage[0]
    await createReview(title, review, artist, userID, author, authorBand, banner, reviewImage)
    navigate('/adminReviews')
} 
    return (
        <>  
            <Form onSubmit={handleSubmit(onSubmit)} encType="multipart/form-data">
                <Form.Label>Create A Review</Form.Label>
                <Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
                    <Form.Label>Title</Form.Label>
                    <Form.Control type="text" placeholder="Title" {...register('title', {required: true })} {...register} />
                </Form.Group>
                <Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
                    <Form.Label>Artist</Form.Label>
                    <Form.Control type="text" placeholder="Artist" {...register('artist', {required: true })} {...register} />
                </Form.Group>
                <Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
                    <Form.Label>Author</Form.Label>
                    <Form.Control type="text" placeholder="Author" {...register('author', {required: true })} {...register} />
                </Form.Group>
                <Form.Group>
                    <Form.Label>Author Band</Form.Label>
                    <Form.Control type="text" placeholder="Author Band" {...register('authorBand', {required: true })} {...register} />
                </Form.Group>
                <Form.Group>
                    <Form.Label>Upload An Image</Form.Label>
                    <Form.Control type="file" placeholder="Upload" {...register('reviewImage', {required: true })} {...register} />
                </Form.Group>
                <Form.Group>
                    <Form.Label>Banner</Form.Label>
                    <Form.Control type="text" placeholder="Banner" {...register('banner', {required: true })} {...register} />
                </Form.Group>
                <Form.Group>
                    <Form.Label>Review</Form.Label>
                    <Form.Control as="textarea" rows={12} {...register('review', {required: true })} {...register} />
                    <>{errors.review?.message}</>
                </Form.Group>
                <Row>
                    <Col xs={12}>
                        <Button type='submit'>Submit</Button>
                    </Col>
                </Row>
            </Form>

useFormSubmit

export const useFormSubmit = () => {
const {dispatch} = useReviewsContext()
const [error, setError] = useState(null)
const [isLoading, setIsLoading] = useState(false)

const createReview = async (title, review, artist, userID, author, authorBand, banner, reviewImage) => {
    const formData = new FormData();
    formData.append("reviewImage", reviewImage);
    setIsLoading(true)
    setError(null)
        const response = await fetch('http://localhost:8080/api/admin/', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({title, review, artist, userID, author, authorBand, banner, formData })
        })
        const jsonResponse = await response.json()
        if(!response.ok) {
            setIsLoading(false)
            setError(jsonResponse.error)
        }
        if(response.ok) {
            setIsLoading(false)
            dispatch({type: 'CREATE_REVIEW', payload: jsonResponse})
    }
}

adminMiddleware.js

const multer = require('multer')

const storage = multer.diskStorage({
    destination: (req, file, callback) => {
        callback(null, '../../client/public/images')
    },
    filename: (req, file, callback) => {
        callback(null, file.originalname)
    }
})

const upload = multer({storage: storage})
module.exports = upload;

adminRoutes.js

const express = require('express')
const router = express.Router();
const upload = require('../middleware/adminMiddleware')

//controllers
const { createReview } = require('../controllers/reviewsController')

//CRUD reviews routes
router.post('/', upload.single('reviewImage'), createReview)


module.exports = router;

reviewsController.js

const createReview = async (req, res) => {
const {title, review, artist, userID, author, authorBand, banner} = req.body
const reviewImage = req.file.reviewImage
let emptyFields = []

if(!title) {
    emptyFields.push('title')
}
if(!review) {
    emptyFields.push('review')
}
if(!artist) {
    emptyFields.push('artist')
}
if(!userID) {
    emptyFields.push('userID')
}
if(!author) {
    emptyFields.push('author')
}
if(!authorBand) {
    emptyFields.push('authorBand')
}
if(!banner) {
    emptyFields.push('banner')
}
if(!reviewImage) {
    emptyFields.push('reviewImage')
}
if(emptyFields.length > 0) {
    return res.status(400).json({ error: 'Please fill in all the fields', emptyFields })
}

// add doc to db
try {
    const createdReview = await Review.create({title, review, artist, userID, author, authorBand, banner, reviewImage})
    res.status(200).json(createdReview)
} catch (error) {
    res.status(400).json({error: error.message})
}
}

Error Messages / Console logs enter image description here enter image description here