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 withtype=file
MUST have a name attribute, this as you will see is correctly specified in mymulter.single("name here")
server side code. - the
hidden
input
field withname=test
I added as a sanity check, this field doesn't show inreq
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 goesnext()
w/o any errors. - when hitting
req.file || req.file === ""
, it always fails and redirects.req.file
andreq.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