OtterJesus
OtterJesus

Reputation: 65

How to get a wav file from a POST body to upload it using Node JS/Express

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

Answers (2)

Lucas S.
Lucas S.

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

shortstuffsushi
shortstuffsushi

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

Related Questions