1

This issue has me stumped. My app requires uploading an excel file with content to add/modify, etc on the platform. For this, I need Multer to upload the excel file where I will store it in os.tempdir() for reading.

Problem:

Using the suggested methods and alternatives, I am unable to upload a file via the browser to my app (running locally). I have tried methods using .single("input file name here") which places the file in req.file, or any() which places the file(s) in req.files. Non of these return a successful file upload, thus req.file is always undefined and req.files is always [].

What code do I have?

Below you will find my webpage & server side code:

Web-page (jade)

...
form(action=`/companies/${companyId}/employees/upload` enctype="multipart/form-data" method="post")
    input(type="file", accept=".xlsx,.xls" , name="uploadEmployees", style="background: transparent" required)
    input(type="hidden" name="test" value="test")
    button#submitUpload.btn.btn-primary(type='submit') Save changes
...

Notes:

  • companyId is defined in my jade template, thus the action URL is complete and correct
  • the enctype type is correct, one should always use multipart/form-data for file uploads
  • the method is correct, I am posting data
  • the input field with type=file MUST have a name attribute, this as you will see is correctly specified in my multer.single("name here") server side code.
  • the hidden input field with name=test I added as a sanity check, this field doesn't show in req

Server-side Code:

const multer = require('multer')
const uploadSpreadsheet = multer().any();

// router prefix is /companies
router.post('/:id/employees/upload', (req, res, next) => {
    uploadSpreadsheet(req, res, (err) => {
        if (err instanceof multer.MulterError) {
            console.log("Multer error")
            console.error(err);
        } else if (err) {
            console.log("Another error")
            console.log(err)
        } else {
            console.log("Multer function success, next()")
            next()
        }
    });
}, async (req, res, next) => {
    console.log("Using multer.any()")
    console.log("req.files should not be empty")
    console.log(`${req.files ? (req.files.length > 0 ? "req.files has some files" : "req.files has no files") : "req.files is undefined"}`)
    console.log(`We should expect to find a hidden field named 'test' with value 'test'`)
    console.log(`${req.body === {} ? "req.body is empty" : req.body["test"] ? "hidden field found" : "no hidden field named 'test' found"}`)
    console.log(req.body);
    if (!req.file || req.file === "") {
        req.flash("message", [{
            status: false,
            message: "No file uploaded or upload failed"
        }]);
        return res.redirect(`/companies/${req.params.id}/employees`)
    }

    // read the entire file
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.readFile(req.file);
    ...

Notes:

  • in an attempt to check Multer errors, I used the Multer error handling as suggested here - see the uploadSpreadsheet(req, res...) part above. This always goes next() w/o any errors.
  • when hitting req.file || req.file === "", it always fails and redirects. req.file and req.files is always either undefined or [] respectively.

Console output:

i  functions: Beginning execution of "us-central1-app"
>  Multer function success, next()
>  Using multer.any()
>  req.files should not be empty
>  req.files has no files
>  We should expect to find a hidden field named 'test' with value 'test'
>  no hidden field named 'test' found
>  [Object: null prototype] {}

Since the line Multer function success, next() appears, one should expect to find file(s) either in req.files or req.file but this doesn't happen.

Any assistance in resolving this issue would be greatly appreciated!


Multer single file upload (for completeness sake)

I define my single file upload function & parameters below:

const os = require('os')
const tmp = os.tmpdir();

function fileFilter(req, file, cb) {
    // for testing
    cb(null, true);
}

var storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, `${tmp}`)
    },
    filename: function (req, file, cb) {
        cb(null, v4())
    }
})
// const uploadSpreadsheet = multer({storage: storage, fileFilter: fileFilter}).single("uploadEmployees");

in the post route, this is what it looks like when using my single file upload:

router.post('/:id/employees/upload', uploadSpreadsheet, async (req, res, next) => {
    ...

client side code remains constant through these above server-side code changes

CybeX
  • 2,060
  • 3
  • 48
  • 115

0 Answers0