Reputation: 65
I am creating a node js app who's purpose is, in part, receiving a POST request and then parsing the body for 2 fields: id and a .wav file.
Unfortunately, I can't seem to parse the req body correctly. I believe the request should be multipart/form-data (which is why I'm using multer and not bodyParser), but I keep getting undefined when I try the log(body.req.file) and the res shows the 500 internal server error I send in the app.post function.
Here is the server code...
Multer part :
var upload = multer({ storage: storage })
var uploadFields = upload.fields([{name: 'id'}, {name: 'file'}])
var storage = multer.diskStorage({
destination: function(req, file, cb){
cb(null, 'temp/')
},
filename: function(req, file, cb){
fileBody = file
keyName = `${req.body.id}.` + getExtension(file)
var objectParams = {Bucket: myBucket, Key: keyName, Body: fileBody}
s3.putObject(objectParams).promise().then(function(res){console.log('wewlad')}).catch(function(err){console.log(err)})
cb(null, file.fieldname + '-' + Date.now() + '.' + getExtension(file))
}
})
Express part:
app.post('/', uploadFields, function(req, res){
console.log('POST to / recieved!')
console.log(req.body.id)
console.log(req.body.file)
fileBody = req.files
keyName = req.body.id + '.wav'
var params = {Bucket: myBucket, Key: keyName, Body: fileBody}
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log(res)
}).catch(function(err){
console.log(err)
})
})
a getExtension function I made (not sure if this is needed)...
function getExtension(file) {
var res = ''
//some of these are being used for testing
if (file.mimetype === 'audio/wav') res = '.wav'
else if (file.mimetype === 'audio/basic') res = '.ul'
else if (file.mimetype === 'multipart/form-data') res = '.wav'
else if (file.mimetype === 'application/octet-stream') res = '.ul'
else console.log(`ERROR: ${file.mimetype} is not supported by this application!`)
return res
}
Side-note: I am using post-man to send the id and file as form-data
Any help is greatly appreciated! Also, if there's anything I left out let me know!
EDIT: Tried again with new info, this is my attempt:
var upload = multer({ dest: 'uploads/' }) //what do I put here to not store file
...
...
app.post('/', upload.single('file'), function(req, res, next){
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
var fileBody = req.file //and what do I put here to get access to the file
var keyName = `audio-${req.body.id}.${getExtension(req.file)}`
var params = { Bucket: myBucket, Key: keyName, Body: fileBody }
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log(res)
res.sendStatus(204);
}).catch(err =>{
console.log(err)
next(err)
})
});
it results in an "InvalidParameterType: Expected params.Body to be a string, buffer, stream, blob, or typed array object" that I is coming from S3 validating the params for the putObject call
Solution for anyone that passes by in the future:
var upload = multer()
app.post('/', upload.single('file'), function(req, res, next){
//for debugging purposes
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
//set the parameters for uploading the files to s3
var fileBody = req.file.buffer
var keyName = `audio_logs/audio-${req.body.id}.${getExtension(req.file)}`
var params = { Bucket: myBucket, Key: keyName, Body: fileBody }
//upload the audio file and then state when done. Log any errors.
var putObjectPromise = s3.putObject(params).promise()
putObjectPromise.then(function(res){
console.log('Uploaded -> ' + res)
}).catch(err =>{
console.log(err)
next(err)
})
});
Upvotes: 0
Views: 3347
Reputation: 2401
As @shortstuffsushi pointed out, since you're using multer.fields
, file metadata is parsed into req.files
.
However, that object is not the actual file body.
It's a key->value map where each key is one of the fields you defined in multer.fields
and each value is an array of file objects.
So if I'm reading your code correctly, you'll have to access your file as req.files['file'][0]
.
Additionally, since you configured multer to use DiskStorage, that file object won't contain the actual file body. Instead, it will have a path
property which points to the file on your file system.
So in order to upload it to S3, you first have to read it from disk into a buffer or stream (depending on what the upload library can use) and then use that to actually upload the file's data.
If you are only uploading a single file per request, you should consider using multer.single
instead of multer.fields
. I suppose the id
field is a number or string, so specifying that in multer.fields
only makes it try to parse nonsense into a file.
And with multer.single
the file metadata will be parsed into req.file
(no 's'), so you don't need the extra "map into array" access.
So a full example would look like this:
const readFile = require('util').promisify(require('fs').readFile)
app.post('/', upload.single('file'), function(req, res, next){
console.log('POST to / received!')
console.log(req.body.id)
console.log(req.file)
readFile(req.file.path)
.then(body => {
const params = { Bucket: myBucket, Key: req.body.id + '.wav', Body: body }
return s3.putObject(params).promise();
})
.then(result => {
console.log(result)
res.sendStatus(204);
})
.catch(err => {
console.error(err);
next(err);
});
});
And just as @shortstuffsushi mentioned, the filename
config for multer should not upload the file as well.
Upvotes: 2
Reputation: 2330
Converting to an answer for your first problem, multer uses the files
property on the request, not body.file
. Check out their docs for more.
Upvotes: 1