How to upload multiple files with additional info inside object

I’m trying to upload multiple files using FormData as an array of objects that contain file and other necessary properties, but I’m not getting expected results.

import api from './api';

const formData = new FormData();

// File input with multiple attribute
const files = this.$refs.photo.files;

if (files.length > 0) {
  for (let i = 0; i < files.length; i++) {
    formData.append('files', { file: files[i], someOtherProp: 'value' });
  }
}

api.createArticle(formData);
// api/index.js

import axios from 'axios';
import config from '@/config';

const api = axios.create({
  baseURL: config.api,
  timeout: config.apiTimeout,
});

createArticle(payload) {
  return api.post('/articles', payload, {
    headers: {
      'Content-Type': 'multipart/form-data',
    }
  });
}

export {
  createArticle
};

When I send request this is what I get

enter image description here

Answer

The documentation of the FormData append function shows that it accepts either two or three parameters (the 3rd being optional).

Of the second parameter – value – it says:

The field’s value. This can be a USVString or Blob (including subclasses such as File). If none of these are specified the value is converted to a string.

Your code tries to pass an object for this parameter, rather than a string or Blob. Therefore FormData is doing exactly what the documentation says it will do that in that situation, and converting that object to a string. [Object object] is what JavaScript always outputs by default if you try to stringify an object directly.

If you wish to append a name for the file specifically, as you can see from the documentation the optional 3rd parameter filename is provided specifically for that purpose.

However if you want to pass some other related property, then you’ll need to do that in a separate field in the FormData.

Also you should not append data items with the same name repeatedly – your server may only see the last one. For that to work the most reliable way you should use array syntax e.g. formData.append("files[]"..., so then the server should see an array of data with that name, rather than a single item. You could do the same for these additional properties you want to pass, then you can also pass multiple items and their index number will match with the index number of the file they’re related to, when your server receives the POSTed data.

For example, something like this should work better:

for (let i = 0; i < files.length; i++) {
  formData.append('files[]', files[i]);
  formData.append('someOtherProp[]', 'value');
}