我怎样才能将我的JS对象转换为FormData?

我这样做的原因是,我有一个用~100个表单字段值构造的对象。

var item = {
   description: 'Some Item',
   price : '0.00',
   srate : '0.00',
   color : 'red',
   ...
   ...
}

现在我被要求将上传文件功能添加到我的表单,当然,通过JSON是不可能的,所以我计划移动到FormData。那么有什么方法可以将我的JS对象转换为FormData呢?


如果您有一个对象,您可以轻松地创建一个FormData对象,并将该对象的名称和值附加到FormData。

你还没有发布任何代码,所以这是一个通用的例子;

var form_data = new FormData();

for ( var key in item ) {
    form_data.append(key, item[key]);
}

$.ajax({
    url         : 'http://example.com/upload.php',
    data        : form_data,
    processData : false,
    contentType : false,
    type: 'POST'
}).done(function(data){
    // do stuff
});

在MDN的文档中有更多的例子


抱歉这么晚才回答,我一直在纠结这个问题,因为Angular 2目前不支持文件上传。方法是用FormData发送一个XMLHttpRequest。我创建了一个函数来做这个。我用的是Typescript。要将其转换为Javascript只需删除数据类型声明。

/**
     * Transforms the json data into form data.
     *
     * Example:
     *
     * Input:
     * 
     * fd = new FormData();
     * dob = {
     *  name: 'phone',
     *  photos: ['myphoto.jpg', 'myotherphoto.png'],
     *  price: '615.99',
     *  color: {
     *      front: 'red',
     *      back: 'blue'
     *  },
     *  buttons: ['power', 'volup', 'voldown'],
     *  cameras: [{
     *      name: 'front',
     *      res: '5Mpx'
     *  },{
     *      name: 'back',
     *      res: '10Mpx'
     *  }]
     * };
     * Say we want to replace 'myotherphoto.png'. We'll have this 'fob'.
     * fob = {
     *  photos: [null, <File object>]
     * };
     * Say we want to wrap the object (Rails way):
     * p = 'product';
     *
     * Output:
     *
     * 'fd' object updated. Now it will have these key-values "<key>, <value>":
     *
     * product[name], phone
     * product[photos][], myphoto.jpg
     * product[photos][], <File object>
     * product[color][front], red
     * product[color][back], blue
     * product[buttons][], power
     * product[buttons][], volup
     * product[buttons][], voldown
     * product[cameras][][name], front
     * product[cameras][][res], 5Mpx
     * product[cameras][][name], back
     * product[cameras][][res], 10Mpx
     * 
     * @param {FormData}  fd  FormData object where items will be appended to.
     * @param {Object}    dob Data object where items will be read from.
     * @param {Object =   null} fob File object where items will override dob's.
     * @param {string =   ''} p Prefix. Useful for wrapping objects and necessary for internal use (as this is a recursive method).
     */
    append(fd: FormData, dob: Object, fob: Object = null, p: string = ''){
        let apnd = this.append;

        function isObj(dob, fob, p){
            if(typeof dob == "object"){
                if(!!dob && dob.constructor === Array){
                    p += '[]';
                    for(let i = 0; i < dob.length; i++){
                        let aux_fob = !!fob ? fob[i] : fob;
                        isObj(dob[i], aux_fob, p);
                    }
                } else {
                    apnd(fd, dob, fob, p);
                }
            } else {
                let value = !!fob ? fob : dob;
                fd.append(p, value);
            }
        }

        for(let prop in dob){
            let aux_p = p == '' ? prop : `${p}[${prop}]`;
            let aux_fob = !!fob ? fob[prop] : fob;
            isObj(dob[prop], aux_fob, aux_p);
        }
    }

使用ES6和更函数式的编程方法@adeneo的答案可能是这样的:

function getFormData(object) {
    const formData = new FormData();
    Object.keys(object).forEach(key => formData.append(key, object[key]));
    return formData;
}

或者使用.reduce()和箭头函数:

const getFormData = object => Object.keys(object).reduce((formData, key) => {
    formData.append(key, object[key]);
    return formData;
}, new FormData());

我有一个场景,在构造表单数据时,嵌套的JSON必须以线性方式序列化,因为这是服务器期望值的方式。所以,我写了一个小的递归函数来翻译JSON,就像这样:

{
   "orderPrice":"11",
   "cardNumber":"************1234",
   "id":"8796191359018",
   "accountHolderName":"Raj Pawan",
   "expiryMonth":"02",
   "expiryYear":"2019",
   "issueNumber":null,
   "billingAddress":{
      "city":"Wonderland",
      "code":"8796682911767",
      "firstname":"Raj Pawan",
      "lastname":"Gumdal",
      "line1":"Addr Line 1",
      "line2":null,
      "state":"US-AS",
      "region":{
         "isocode":"US-AS"
      },
      "zip":"76767-6776"
   }
}

变成这样:

{
   "orderPrice":"11",
   "cardNumber":"************1234",
   "id":"8796191359018",
   "accountHolderName":"Raj Pawan",
   "expiryMonth":"02",
   "expiryYear":"2019",
   "issueNumber":null,
   "billingAddress.city":"Wonderland",
   "billingAddress.code":"8796682911767",
   "billingAddress.firstname":"Raj Pawan",
   "billingAddress.lastname":"Gumdal",
   "billingAddress.line1":"Addr Line 1",
   "billingAddress.line2":null,
   "billingAddress.state":"US-AS",
   "billingAddress.region.isocode":"US-AS",
   "billingAddress.zip":"76767-6776"
}

服务器将接受这种转换格式的表单数据。

函数如下:

function jsonToFormData (inJSON, inTestJSON, inFormData, parentKey) {
    // http://stackoverflow.com/a/22783314/260665
    // Raj: Converts any nested JSON to formData.
    var form_data = inFormData || new FormData();
    var testJSON = inTestJSON || {};
    for ( var key in inJSON ) {
        // 1. If it is a recursion, then key has to be constructed like "parent.child" where parent JSON contains a child JSON
        // 2. Perform append data only if the value for key is not a JSON, recurse otherwise!
        var constructedKey = key;
        if (parentKey) {
            constructedKey = parentKey + "." + key;
        }

        var value = inJSON[key];
        if (value && value.constructor === {}.constructor) {
            // This is a JSON, we now need to recurse!
            jsonToFormData (value, testJSON, form_data, constructedKey);
        } else {
            form_data.append(constructedKey, inJSON[key]);
            testJSON[constructedKey] = inJSON[key];
        }
    }
    return form_data;
}

调用:

        var testJSON = {};
        var form_data = jsonToFormData (jsonForPost, testJSON);

我使用testJSON只是为了查看转换后的结果,因为我无法提取form_data的内容。AJAX post call:

        $.ajax({
            type: "POST",
            url: somePostURL,
            data: form_data,
            processData : false,
            contentType : false,
            success: function (data) {
            },
            error: function (e) {
            }
        });

该函数将object中的所有数据添加到FormData中

ES6版本来自@developer033:

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

    formData.append(parentKey, value);
  }
}

function jsonToFormData(data) {
  const formData = new FormData();
  
  buildFormData(formData, data);
  
  return formData;
}

const my_data = {
  num: 1,
  falseBool: false,
  trueBool: true,
  empty: '',
  und: undefined,
  nullable: null,
  date: new Date(),
  name: 'str',
  another_object: {
    name: 'my_name',
    value: 'whatever'
  },
  array: [
    {
      key1: {
        name: 'key1'
      }
    }
  ]
};

jsonToFormData(my_data)

jQuery版本:

function appendFormdata(FormData, data, name){
    name = name || '';
    if (typeof data === 'object'){
        $.each(data, function(index, value){
            if (name == ''){
                appendFormdata(FormData, value, index);
            } else {
                appendFormdata(FormData, value, name + '['+index+']');
            }
        })
    } else {
        FormData.append(name, data);
    }
}


var formData = new FormData(),
    your_object = {
        name: 'test object',
        another_object: {
            name: 'and other objects',
            value: 'whatever'
        }
    };
appendFormdata(formData, your_object); 

其他的答案对我来说是不完整的。我从@Vladimir Novopashin的回答开始修改。以下是我需要的东西和我发现的bug:

文件支持 支持数组 Bug:复杂对象内的文件需要添加。prop而不是[prop]。例如,formData。附加('照片[0][文件]',文件)不工作在谷歌chrome,而 formData.append('照片[0]。文件',文件)工作 忽略对象中的某些属性

下面的代码应该可以在IE11和常绿浏览器上运行。

function objectToFormData(obj, rootName, ignoreList) {
    var formData = new FormData();

    function appendFormData(data, root) {
        if (!ignore(root)) {
            root = root || '';
            if (data instanceof File) {
                formData.append(root, data);
            } else if (Array.isArray(data)) {
                for (var i = 0; i < data.length; i++) {
                    appendFormData(data[i], root + '[' + i + ']');
                }
            } else if (typeof data === 'object' && data) {
                for (var key in data) {
                    if (data.hasOwnProperty(key)) {
                        if (root === '') {
                            appendFormData(data[key], key);
                        } else {
                            appendFormData(data[key], root + '.' + key);
                        }
                    }
                }
            } else {
                if (data !== null && typeof data !== 'undefined') {
                    formData.append(root, data);
                }
            }
        }
    }

    function ignore(root){
        return Array.isArray(ignoreList)
            && ignoreList.some(function(x) { return x === root; });
    }

    appendFormData(obj, rootName);

    return formData;
}

尝试 obj2fd=> https://www.npmjs.com/package/obj2fd

import obj2fd from 'obj2fd'

let data = {a:1, b:2, c:{ca:1}};
let dataWithFormData = obj2fd(data);
//result => [a=>1, b=>2, c=>[ca=>1]]

尝试JSON。函数如下所示

var postData = JSON.stringify(item);
var formData = new FormData();
formData.append("postData",postData );

这个方法将一个JS对象转换为一个FormData:

function convertToFormData(params) { return Object.entries(params) .reduce((acc, [key, value]) => { if (Array.isArray(value)) { value.forEach((v, k) => acc.append(`${key}[${k}]`, value)); } else if (typeof value === 'object' && !(value instanceof File) && !(value instanceof Date)) { Object.entries(value).forEach((v, k) => acc.append(`${key}[${k}]`, value)); } else { acc.append(key, value); } return acc; }, new FormData()); }


在我的例子中,我的对象也有属性,即文件数组。因为它们是二进制的,所以应该以不同的方式处理——索引不需要是键的一部分。所以我修改了@Vladimir Novopashin和@developer033的答案:

export function convertToFormData(data, formData, parentKey) {
  if(data === null || data === undefined) return null;

  formData = formData || new FormData();

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

  return formData;
}

你可以简单地安装qs:

npm i qs

简单的导入:

import qs from 'qs'

将对象传递给qs.stringify():

var item = {
   description: 'Some Item',
   price : '0.00',
   srate : '0.00',
   color : 'red',
   ...
   ...
}

qs.stringify(item)

递归地

const toFormData = (f => f(f))(h => f => f(x => h(h)(f)(x)))(f => fd => pk => d => { if (d instanceof Object) { Object.keys(d).forEach(k => { const v = d[k] if (pk) k = `${pk}[${k}]` if (v instanceof Object && !(v instanceof Date) && !(v instanceof File)) { return f(fd)(k)(v) } else { fd.append(k, v) } }) } return fd })(new FormData())() let data = { name: 'John', age: 30, colors: ['red', 'green', 'blue'], children: [ { name: 'Max', age: 3 }, { name: 'Madonna', age: 10 } ] } console.log('data', data) document.getElementById("data").insertAdjacentHTML('beforeend', JSON.stringify(data)) let formData = toFormData(data) for (let key of formData.keys()) { console.log(key, formData.getAll(key).join(',')) document.getElementById("item").insertAdjacentHTML('beforeend', `<li>${key} = ${formData.getAll(key).join(',')}</li>`) } <p id="data"></p> <ul id="item"></ul>


我使用这个Post我的对象数据作为表单数据。

const encodeData = require('querystring');

const object = {type: 'Authorization', username: 'test', password: '123456'};

console.log(object);
console.log(encodeData.stringify(object));

也许你正在寻找这个,一个代码,接收你的javascript对象,从它创建一个FormData对象,然后POST它到你的服务器使用新的Fetch API:

    let myJsObj = {'someIndex': 'a value'};

    let datos = new FormData();
    for (let i in myJsObj){
        datos.append( i, myJsObj[i] );
    }

    fetch('your.php', {
        method: 'POST',
        body: datos
    }).then(response => response.json())
        .then(objson => {
            console.log('Success:', objson);
        })
        .catch((error) => {
            console.error('Error:', error);
        });

我参考了古德拉丹的回答。我用Typescript格式编辑了一下。

class UtilityService {
    private appendFormData(formData, data, rootName) {

        let root = rootName || '';
        if (data instanceof File) {
            formData.append(root, data);
        } else if (Array.isArray(data)) {
            for (var i = 0; i < data.length; i++) {
                this.appendFormData(formData, data[i], root + '[' + i + ']');
            }
        } else if (typeof data === 'object' && data) {
            for (var key in data) {
                if (data.hasOwnProperty(key)) {
                    if (root === '') {
                        this.appendFormData(formData, data[key], key);
                    } else {
                        this.appendFormData(formData, data[key], root + '.' + key);
                    }
                }
            }
        } else {
            if (data !== null && typeof data !== 'undefined') {
                formData.append(root, data);
            }
        }
    }

    getFormDataFromObj(data) {
        var formData = new FormData();

        this.appendFormData(formData, data, '');

        return formData;
    }
}

export let UtilityMan = new UtilityService();

我可能有点晚了,但这就是我创建的将单个对象转换为FormData的方法。

function formData(formData, filesIgnore = []) {
  let data = new FormData();

  let files = filesIgnore;

  Object.entries(formData).forEach(([key, value]) => {
    if (typeof value === 'object' && !files.includes(key)) {
      data.append(key, JSON.stringify(value) || null);
    } else if (files.includes(key)) {
      data.append(key, value[0] || null);
    } else {
      data.append(key, value || null);
    }
  })

  return data;
}

它是如何工作的? 它将转换并返回所有属性,除了您在忽略列表中设置的File对象(第二个参数)。如果有人能告诉我一个更好的方法来确定这将有帮助!)变成一个json字符串使用json .stringify。然后在您的服务器上,您只需要将其转换回JSON对象。

例子:

let form = {
  first_name: 'John',
  last_name: 'Doe',
  details: {
    phone_number: 1234 5678 910,
    address: '123 Some Street',
  },
  profile_picture: [object FileList] // set by your form file input. Currently only support 1 file per property.
}

function submit() {
  let data = formData(form, ['profile_picture']);

  axios.post('/url', data).then(res => {
    console.log('object uploaded');
  })
}

我仍然有点新的Http请求和JavaScript,所以任何反馈将高度赞赏!


下面是一个使用Object.entries()的简单而甜蜜的解决方案,它甚至可以处理嵌套对象。

// If this is the object you want to convert to FormData...
const item = {
    description: 'First item',
    price: 13,
    photo: File
};

const formData = new FormData();

Object.entries(item).forEach(([key, value]) => {
    formData.append(key, value);
});

// At this point, you can then pass formData to your handler method

阅读更多关于Object.entries()在这里- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries


2022年更新

Axios现在支持将对象自动序列化为FormData

从0.27版开始,如果请求的Content-Type报头被设置为multipart/form-data, Axios就支持将对象自动序列化为FormData对象。阅读更多

嵌套对象和文件

下面的解决方案处理嵌套对象、数组和文件。

const buildFormData = (formData: FormData, data: FormVal, parentKey?: string) => {
    if (Array.isArray(data)) {
        data.forEach((el) => {
            buildFormData(formData, el, parentKey)
        })

    } else if (typeof data === "object" && !(data instanceof File)) {
        Object.keys(data).forEach((key) => {
            buildFormData(formData, (data as FormDataNest)[key], parentKey ? `${parentKey}.${key}` : key)
        })

    } else {
        if (isNil(data)) {
            return
        }

        let value = typeof data === "boolean" || typeof data === "number" ? data.toString() : data
        formData.append(parentKey as string, value)
    }
}

export const getFormData = (data: Record<string, FormDataNest>) => {
    const formData = new FormData()

    buildFormData(formData, data)

    return formData
}

类型

type FormDataPrimitive = string | Blob | number | boolean

interface FormDataNest {
  [x: string]: FormVal
}

type FormVal = FormDataNest | FormDataPrimitive

你可以简单地使用:

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

很简单,可以这样做:

var item: { some1: "ajbd" , some2: "dds".. }
let myFormData = new FormData();
      
       const abc = item.some1;
       const xyz = item.some2;

          myFormData.append('field1', abc);
          myFormData.append('field2', xyz);
    
    fetch('http:url', {
      method: 'POST',
      headers: {
        'Content-Type': false,
      },
      body: myFormData,
    }).
do promise ..

函数toFormData(o) { 返回Object.entries (o) .reduce ((d, e) = > (d.append e(…),d),新FormData ()) } Var对象= { 用户名:“JohnDoe”, 文件:新文件(“foo”,“foo.txt”,{类型:“文本/普通”}) } fetch (https://httpbin.org/post, { 方法:“文章”, 身体:toFormData(对象) })。Then (r => r.json()).then(console.log)


注意:这个答案并没有直接回答问题,而是给出了说明和替代方案。

人们通常使用FormData来允许他们上传文件,有三种方法可以做到这一点,我会在这个回答中详细提到它们。

1. 直接从表单中获取数据

为此,您需要在每个输入中拥有名称和值属性,然后告诉FormData从表单DOM中获取值;

let formData = new FormData(data.target as HTMLFormElement)

优点:

你不需要使用JS解决方案来获取这些值。 原生支持嵌套数据,数组等…

缺点:

您需要确保所有需要的数据都作为值属性添加到输入dom上

2. 用JS转换数据 如果你把值存储在变量中,那么你可以使用JS将它们附加到FormData中。

优点:

是否可以根据需要进行操作 你不需要在输入dom中有值属性

缺点:

自定义复杂JS附加嵌套数据到FormData对象 您需要将值存储在一个变量中

3.在单独的请求中上传文件

您可以为文件上传输入创建一个单独的表单。

优点:

您不需要等待上传提交 您不需要担心将所有数据追加到FormData对象,您只追加文件。

缺点:

在创建实体本身之前,您需要在后端处理这些上传 您需要在前端和后端处理删除文件


使用jquery,你可以通过$.param(obj)简单地做到这一点。

例子: Const obj = { 描述:'Some Item', 价格:0.00, srate:“0.00”, 颜色:红色的 } Const form_obj = $.param(obj); . ajax({美元 url:“example.com”, 方法:“文章”, 数据:form_obj }) < script src = " https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js " > < /脚本>


将处理嵌套数组和对象的解决方案。有人可能会觉得有用

             function add_form_data(form_data,key,item,arr){
                if(typeof(item)==='object' && item && item.constructor===Array){
                    for(var i=0;i<item.length;i++){
                        var item2=item[i];
                        var key2=key+'[' + i +']';
                        if(arr){
                            key2=key+'[' + key2 +']';
                        }
                        add_form_data(form_data,key2,item2,true);
                    }
                }else if(typeof(item)==='object' && item){
                    for ( var key2 in item ) {
                        var item2=item[key2];
                        if(arr){
                            key2=key+'[' + key2 +']';
                        }
                        add_form_data(form_data,key2,item2,arr);
                    }
                }else{
                    form_data.append(key,item);
                }
            }

使用

            var form_data = new FormData();
            
            add_form_data(form_data,null,json_data);// provide json_data here
             
            var string_url_data=new URLSearchParams(form_data).toString();// if query string is needed

下面是一个非常简单的TypeScript实现,基于@Vladimir Novopashin和@developer033的回答。打印稿操场

type Serializeable =
    | string
    | number
    | boolean
    | null
    | Date
    | File
    | { [x: string | number]: Serializeable }
    | Array<Serializeable>;

function serialize(
    data: Serializeable,
    parentKey = '',
    formData: FormData = new FormData()
): FormData {
    if ( typeof data === 'string') {
        formData.append(parentKey, data);
    } else if ( typeof data === 'number') {
        formData.append(parentKey, data.toString());
    } else if ( typeof data === 'boolean') {
        formData.append(parentKey, data ? 'true' : 'false');
    } else if (data === null) {
        formData.append(parentKey, 'null');
    } else if (data instanceof Date) {
        formData.append(parentKey, data.toISOString());
    } else if (data instanceof File) {
        formData.append(parentKey, data);
    } else {
        // Arrays and objects
        Object.entries(data).forEach((entry: [string | number, Serializeable]) => {
            const [key, value] = entry;
            serialize(value, parentKey ? `${parentKey}[${key}]` : key.toString(), formData);
        });
    }

    return formData;
}