5

I'm having some trouble with using the enctype multipart/form-data while at the same time sending inputs with the same name to be obtained as an array. I can only seem to obtain an uploaded image OR the array inputs, but not both at the same time...

For example, I have this form:

<form method="post" action="/test">
  <input name="testinput" value="valueA">
  <input name="testinput" value="valueB">
  <input type="file" name="fileattachment">
  <input type="submit" value="Submit">
</form>

If I set the form's enctype to be multipart/form-data, like this:

<form method="post" action="/test" enctype="multipart/form-data">

I end up receiving the 'fileattachment' just fine in my NodeJS app, BUT i only get the last value for 'testinput', like this:

//req.body
//---
{
    testinput: 'valueB' // I'm missing valueA!
}

//req.files
//---
{
    fileattachment: { 
        name: 'biglogo.png',
        data: <Buffer 89 ... >,
        encoding: '7bit',
        mimetype: 'image/png',
        mv: [Function] 
    }
}

If the enctype is not set, the 'testinput' data comes as an array, BUT the 'fileattachment' is lost and I only get the name of the uploaded file, like this:

//req.body
//---
{
    testinput: ['valueA', 'valueB'],
    fileattachment: 'some_picture.png' // Useless for file uploading
}

I think it has something to do with the way I've set up express' body parser, but I can't seem to figure out the right configuration. This is my setup (simplified for the relevant code):

var express = require('express');
var fileUpload = require('express-fileupload');
var bodyParser = require('body-parser');

var app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(fileUpload()); // Must be placed after, not before, bodyparser's use, otherwise files fail to be uploaded correctly...

app.post('/test', function(req, res) { 
    // Some code
});

Also, this is my package.json file:

{
    "name": "my-app",
    ...
    "dependencies": {
        "body-parser": "~1.15",
        "express": "~4.14",
        "express-fileupload": "^0.0.5"
    }
}

This is running on node/6.9.1

I've seen this very similar question Multipart/form-data with arrays, but it is 2 years old, unanswered and doesn't seem to use the dependency fileUpload.

Also, I tried the approach proposed by the answer to this question Handling input arrays in Express forms?, but all I keep getting on the server looks is text instead of arrays, like this:

{ 
    'something[0][testinput]': 'valueA',
    'something[1][testinput]': 'valueB'
}

What am I missing? What should I try?

cavpollo
  • 4,071
  • 2
  • 40
  • 64

3 Answers3

1

I was able to obtain the desired result by switching from express-fileupload to Multiparty

The setup:

var express = require('express');
var bodyParser = require('body-parser');

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

My code:

var multiparty = require('multiparty');
app.post('/test', function(req, res) { 
    (new multiparty.Form()).parse(req, function(err, fields, files) {
        // handling fields and files code
    });
});

Fields:

{ 
    testinput: ['valueA', 'valueB']
}

Files:

{ 
    fileattachment: [ 
        { 
            fieldName: 'fileattachment',
            originalFilename: 'biglogo.png',
            path: '/tmp/blablaasdfgh.png',
            headers: [Object],
            size: 10130 
        } 
    ]
}

As you can see the inputs are bundled together on an array, and the file seems to be correctly recieved.

cavpollo
  • 4,071
  • 2
  • 40
  • 64
1

Configure the express-fileupload library to use the parseNested property like this:

const fileUpload = require('express-fileupload');
app.use(fileUpload({
    parseNested: true // magic
}));                  
cavpollo
  • 4,071
  • 2
  • 40
  • 64
yash
  • 253
  • 7
  • 17
  • Haven't been able to use your code as I no longer maintain the original project, but the solution looks quite adequate =) – cavpollo May 19 '19 at 07:48
0

I had the same problem using express-fileupload, I was able to make it work with this function

ProjectController.prototype.teste = async function (request, response, next) {
    let documents = parseFormDate(request.body, request.files);
    return response.status(200).json({ 'body': request.body, 'documents': documents });
};

and

const parseFormDate = (field, files) => {
    let documents = []
    for (const key in files) {
        if (files.hasOwnProperty(key)) {
            const file = files[key];
            if (field[key]) {
                documents.push({ title: field[key], document: file })
                delete field[key];
            }

            delete files[key];
        }
    }

    return documents;
}
cavpollo
  • 4,071
  • 2
  • 40
  • 64