Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaScript Object to FormData, with support for nested objects, arrays and File objects. Includes Angular.js usage.
// takes a {} object and returns a FormData object
var objectToFormData = function(obj, form, namespace) {
var fd = form || new FormData();
var formKey;
for(var property in obj) {
if(obj.hasOwnProperty(property)) {
if(namespace) {
formKey = namespace + '[' + property + ']';
} else {
formKey = property;
}
// if the property is an object, but not a File,
// use recursivity.
if(typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
objectToFormData(obj[property], fd, property);
} else {
// if it's a string or a File object
fd.append(formKey, obj[property]);
}
}
}
return fd;
};
// usage example
var z = objectToFormData({
obj: {
prop: 'property value'
},
arr: [
'one',
'two',
'three',
new File([''], '')
],
file: new File([''], '')
});
var xhr = new XMLHttpRequest;
xhr.open('POST', '/', true);
xhr.send(z);
// usage for Angular.js
// wrap object to formdata method,
// to use it as a transform with angular's http.
var formDataTransform = function(data, headersGetter) {
// we need to set Content-Type to undefined,
// to make the browser set it to multipart/form-data
// and fill in the correct *boundary*.
// setting Content-Type to multipart/form-data manually
// will fail to fill in the boundary parameter of the request.
headersGetter()['Content-Type'] = undefined;
return objectToFormData(data);
};
$http({
method: 'POST',
url: '/',
transformRequest: formDataTransform,
data: { your_object: {} }
})
.success(function(res) {});
@maurotrigo

This comment has been minimized.

Copy link

maurotrigo commented Aug 10, 2015

I think it needs a small correction, when passing the namespace param on the objectToFormData function, it should be formKey instead of property.

if(typeof obj[property] === 'object' && !(obj[property] instanceof File)) {

    objectToFormData(obj[property], fd, formKey);

} else {

    // if it's a string or a File object
    fd.append(formKey, obj[property]);
}
@dflourusso

This comment has been minimized.

Copy link

dflourusso commented Nov 27, 2015

Very good, thanks.

@romeboards

This comment has been minimized.

Copy link

romeboards commented Jan 24, 2016

Something else to note: it seems to break down when passing in complex objects like Date.

I tweaked the conditional to simply include ... && !(obj[property] instanceof Date)) but I'm guessing it'll do the same for other built-in objects

@isidroamv

This comment has been minimized.

Copy link

isidroamv commented May 9, 2016

You should consider append object value to formData only if there is a value, at line 8:

if(obj.hasOwnProperty(property) && obj[property] ) {
....
@khacluan

This comment has been minimized.

Copy link

khacluan commented Sep 7, 2016

nice 👍

@adamtal3

This comment has been minimized.

Copy link

adamtal3 commented Jan 8, 2017

Considering the comments this is what I got:

function toFormData(obj, form, namespace) {
  let fd = form || new FormData();
  let formKey;
  
  for(let property in obj) {
    if(obj.hasOwnProperty(property) && obj[property]) {
      if (namespace) {
        formKey = namespace + '[' + property + ']';
      } else {
        formKey = property;
      }
     
      // if the property is an object, but not a File, use recursivity.
      if (obj[property] instanceof Date) {
        fd.append(formKey, obj[property].toISOString());
      }
      else if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
        toFormData(obj[property], fd, formKey);
      } else { // if it's a string or a File object
        fd.append(formKey, obj[property]);
      }
    }
  }
  
  return fd;
}
@MidnightDesign

This comment has been minimized.

Copy link

MidnightDesign commented Feb 14, 2017

Here's a TypeScript version:

function createFormData(object: Object, form?: FormData, namespace?: string): FormData {
    const formData = form || new FormData();
    for (let property in object) {
        if (!object.hasOwnProperty(property) || !object[property]) {
            continue;
        }
        const formKey = namespace ? `${namespace}[${property}]` : property;
        if (object[property] instanceof Date) {
            formData.append(formKey, object[property].toISOString());
        } else if (typeof object[property] === 'object' && !(object[property] instanceof File)) {
            createFormData(object[property], formData, formKey);
        } else {
            formData.append(formKey, object[property]);
        }
    }
    return formData;
}
@IgorHorta

This comment has been minimized.

Copy link

IgorHorta commented Jun 2, 2017

@adamtal3 @MidnightDesign if (!object.hasOwnProperty(property) || !object[property]) is problematic if the object property is 0 or a false boolean ..
it should be replaced by if (obj.hasOwnProperty(property) && obj[property] != null && obj[property] !== undefined).
anyway your code saved my day .. thx

@yeonhoyoon

This comment has been minimized.

@athlonUA

This comment has been minimized.

Copy link

athlonUA commented Jun 19, 2017

👍

@silkyland

This comment has been minimized.

Copy link

silkyland commented Jun 21, 2017

You solved my life, awesome man!

@R-iskey

This comment has been minimized.

Copy link

R-iskey commented Aug 8, 2017

Thank you bro!

@therealparmesh

This comment has been minimized.

Copy link

therealparmesh commented Sep 20, 2017

I'm going to point to my own solution for this. 😄

@JZLeung

This comment has been minimized.

Copy link

JZLeung commented Oct 10, 2017

👍. Got it, thx!

@BlinZeka

This comment has been minimized.

Copy link

BlinZeka commented Nov 21, 2017

Saved my life, thanks bro!

@EngrKhizarIqbal

This comment has been minimized.

Copy link

EngrKhizarIqbal commented Dec 31, 2017

You saved my life. Thanks a lot to all of them who contributed in the gist.

@donjajo

This comment has been minimized.

Copy link

donjajo commented Jan 4, 2018

Live saving!!!
Thank you! :)

@RocKhalil

This comment has been minimized.

Copy link

RocKhalil commented Feb 7, 2018

There's an issue with Arrays;
You'll need to add a condition for array, something like this:

if (obj[property] instanceof Date) {
  fd.append(formKey, obj[property].toISOString());
} else if (obj[property] instanceof Array) {
  formKey = formKey + '[]'
  obj[property].forEach(element => {
    fd.append(formKey, obj[property]);
  })
} else if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
  this.toFormData(obj[property], fd, formKey);
} else { // if it's a string or a File object
  fd.append(formKey, obj[property]);
}
@Mds92

This comment has been minimized.

Copy link

Mds92 commented Mar 20, 2018

Full version with supporting array

export class Utility {      
    public static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
        let formData = form || new FormData();
        let formKey;

        for (let propertyName in model) {
            if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
            let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
            if (model[propertyName] instanceof Date)
                formData.append(formKey, model[propertyName].toISOString());
            else if (model[propertyName] instanceof Array) {
                model[propertyName].forEach((element, index) => {
                    const tempFormKey = `${formKey}[${index}]`;
                    this.convertModelToFormData(element, formData, tempFormKey);
                });
            }
            else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File))
                this.convertModelToFormData(model[propertyName], formData, formKey);
            else
                formData.append(formKey, model[propertyName].toString());
        }
        return formData;
    }
}
@Flexiink

This comment has been minimized.

Copy link

Flexiink commented Apr 27, 2018

Mds92 Thank you! :)

@RudeySH

This comment has been minimized.

Copy link

RudeySH commented May 29, 2018

@RocKhalil The array handling you're suggesting is wrong, and not even needed. What if the array contains objects? The array handling should recursively call objectToFormData for each element instead of immediately appending the values to the formdata. Which is exactly what the object condition is already doing. And guess what? An array is an object. So you don't need an array condition at all!

@aharonoh

This comment has been minimized.

Copy link

aharonoh commented Jun 12, 2018

Mds92 Thank you! :)

@humoyun

This comment has been minimized.

Copy link

humoyun commented Aug 22, 2018

@Mds92 Awesome. Thank you!

@denisloshkarev

This comment has been minimized.

Copy link

denisloshkarev commented Sep 29, 2018

I'm little change previous class to fix of adding of Array with basic type's items like numbers to FormData. Also this one is good to use for convert the Vue.js Observer objects to the FormData

export default class Helpers {
    static convertModelToFormData(data = {}, form = null, namespace = '') {
        let files = {};
        let model = {};
        for (let propertyName in data) {
            if (data.hasOwnProperty(propertyName) && data[propertyName] instanceof File) {
                files[propertyName] = data[propertyName]
            } else {
                model[propertyName] = data[propertyName]
            }
        }

        model = JSON.parse(JSON.stringify(model))
        let formData = form || new FormData();

        for (let propertyName in model) {
            if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
            let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
            if (model[propertyName] instanceof Date)
                formData.append(formKey, model[propertyName].toISOString());
            else if (model[propertyName] instanceof File) {
                formData.append(formKey, model[propertyName]);
            }
            else if (model[propertyName] instanceof Array) {
                model[propertyName].forEach((element, index) => {
                    const tempFormKey = `${formKey}[${index}]`;
                    if (typeof element === 'object') this.convertModelToFormData(element, formData, tempFormKey);
                    else formData.append(tempFormKey, element.toString());
                });
            }
            else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File))
                this.convertModelToFormData(model[propertyName], formData, formKey);
            else {
                formData.append(formKey, model[propertyName].toString());
            }
        }

        for (let propertyName in files) {
            if (files.hasOwnProperty(propertyName)) {
                formData.append(propertyName, files[propertyName]);
            }
        }
        return formData;
    }
}
@MilosStanic

This comment has been minimized.

Copy link

MilosStanic commented Dec 8, 2018

Here's a TypeScript version:

function createFormData(object: Object, form?: FormData, namespace?: string): FormData {
    const formData = form || new FormData();
    for (let property in object) {
        if (!object.hasOwnProperty(property) || !object[property]) {
            continue;
        }
        const formKey = namespace ? `${namespace}[${property}]` : property;
        if (object[property] instanceof Date) {
            formData.append(formKey, object[property].toISOString());
        } else if (typeof object[property] === 'object' && !(object[property] instanceof File)) {
            createFormData(object[property], formData, formKey);
        } else {
            formData.append(formKey, object[property]);
        }
    }
    return formData;
}

@MidnightDesign I had to make a change in the first if of your code, because it was ignoring false booleans:

if (!object.hasOwnProperty(property)/*  || !object[property] */) {
            continue;
        }
@zernie

This comment has been minimized.

Copy link

zernie commented Dec 9, 2018

@denisloshkarev I had to remove model = JSON.parse(JSON.stringify(model)); line, because it converted nested File instances into objects.

@Peyu

This comment has been minimized.

Copy link

Peyu commented Jan 9, 2019

When used with some complex objects, sometimes you get some values concateneted. To prevent this behavior I add some lines at the last "else" statement:

                   if (obj[property] instanceof Date) {
                        ...
                        ...
                        } else { // if it's a string or a File object
    
                        var alreadySaved = false;
                        for (var pair of fd.entries()) {
                            if((pair[0] == formKey &&  obj[property] == pair[1])){
                                alreadySaved = true;
                            }; 
                        }

                        if(!alreadySaved){
                            fd.append(formKey, obj[property]);
                        }
                         
                    }`
@bahmansh

This comment has been minimized.

Copy link

bahmansh commented Jul 9, 2019

Here's a TypeScript version:

function createFormData(object: Object, form?: FormData, namespace?: string): FormData {
    const formData = form || new FormData();
    for (let property in object) {
        if (!object.hasOwnProperty(property) || !object[property]) {
            continue;
        }
        const formKey = namespace ? `${namespace}[${property}]` : property;
        if (object[property] instanceof Date) {
            formData.append(formKey, object[property].toISOString());
        } else if (typeof object[property] === 'object' && !(object[property] instanceof File)) {
            createFormData(object[property], formData, formKey);
        } else {
            formData.append(formKey, object[property]);
        }
    }
    return formData;
}

Good job man.
Thanks.

@cdrandin

This comment has been minimized.

Copy link

cdrandin commented Sep 13, 2019

Also, in case it isn't clear and people want to pass other file-like data or binary stuff. Replace File with Blob.

@avrail

This comment has been minimized.

Copy link

avrail commented Sep 20, 2019

if you have a complex object that contains file will not work
you need to do something like this


 // if the property is an object, but not a File, use recursivity.
                if (obj[property] instanceof Date) {
                    fd.append(formKey, obj[property].toISOString());
                }
                else if (typeof obj[property] === 'object' && !(obj[property] instanceof File)) {
                    this.toFormData(obj[property], fd, formKey);
                } else {
                    if (obj[property] instanceof File) {
                        //to add file.
                        formKey = formKey.replace('[' + property + ']', '.' + property);
                    }
                    // if it's a string or a File object
                    fd.append(formKey, obj[property]);
                }
@huypvw

This comment has been minimized.

Copy link

huypvw commented Dec 27, 2019

Hi, thanks, is very helpful. But it seem have a issue when object nested multi level. I think at line 20, it should become: objectToFormData(obj[property], fd, formKey);

@mencerz

This comment has been minimized.

Copy link

mencerz commented Feb 3, 2020

I'm little change previous class to fix of adding of Array with basic type's items like numbers to FormData. Also this one is good to use for convert the Vue.js Observer objects to the FormData

export default class Helpers {
    static convertModelToFormData(data = {}, form = null, namespace = '') {
        let files = {};
        let model = {};
        for (let propertyName in data) {
            if (data.hasOwnProperty(propertyName) && data[propertyName] instanceof File) {
                files[propertyName] = data[propertyName]
            } else {
                model[propertyName] = data[propertyName]
            }
        }

        model = JSON.parse(JSON.stringify(model))
        let formData = form || new FormData();

        for (let propertyName in model) {
            if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
            let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
            if (model[propertyName] instanceof Date)
                formData.append(formKey, model[propertyName].toISOString());
            else if (model[propertyName] instanceof File) {
                formData.append(formKey, model[propertyName]);
            }
            else if (model[propertyName] instanceof Array) {
                model[propertyName].forEach((element, index) => {
                    const tempFormKey = `${formKey}[${index}]`;
                    if (typeof element === 'object') this.convertModelToFormData(element, formData, tempFormKey);
                    else formData.append(tempFormKey, element.toString());
                });
            }
            else if (typeof model[propertyName] === 'object' && !(model[propertyName] instanceof File))
                this.convertModelToFormData(model[propertyName], formData, formKey);
            else {
                formData.append(formKey, model[propertyName].toString());
            }
        }

        for (let propertyName in files) {
            if (files.hasOwnProperty(propertyName)) {
                formData.append(propertyName, files[propertyName]);
            }
        }
        return formData;
    }
}

There has bug, when property string and have value equal "" then this property not be exist in formData
data.my_input = '';
..
do this =) ...
..
formData.get('my_input') -> null

@yowee

This comment has been minimized.

Copy link

yowee commented Apr 11, 2020

You know what I did? I just sent the data as a String and I received it as a Json example

Client Side
let ageGroupTarget= { age: { minAge: this.myForm.get('minAge').value, maxAge: this.myForm.get('maxAge').value } }

formData.append('ageGroupTarget', JSON.stringify(ageGroupTarget));

server Side

JSON.parse(req.body.targetFollowers);

@bigabdoul

This comment has been minimized.

Copy link

bigabdoul commented Aug 5, 2020

Nice piece of code but it doesn't work for all scenarios. What about the following object?

{
  "id": 21,
  "displayName": "User",
  "email": "user@example.com",
  "firstName": "First name",
  "lastName": "Last name",
  "isActive": true,
  "createdBy": 52,
  "creationDate": "2018-07-23T11:45:15.77",
  "note": "",
  "profileId": 2,
  "userName": "username",
  "sendConfirmationEmail": false,
  "selectedRoles": [
    { "id": 7, "name": "Administrator", "selected": false },
    { "id": 1, "name": "Entry clerk", "selected": true },
    { "id": 2, "name": "Data entry supervisor", "selected": false }
  ],
  "moduleId": null,
  "competencies": [48, 44, 6],
  "originalCompetencies": [],
  "jobTitle": null,
  "departmentName": null,
  "selectedModuleId": 0,
  "fullName": "Full user name"
}

On the other hand, this code from an answer on SO does the job:

function buildFormData(formData, data, parentKey) {
  if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File) && !(data instanceof Blob)) {
    Object.keys(data).forEach(key => {
      buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
    });
  } else {
    const value = data == null ? '' : data;

    formData.append(parentKey, value);
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.