将JS对象转换为表单数据

Kam*_*med 93 javascript jquery multipartform-data form-data

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

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

var item = {
   description: 'Some Item',
   price : '0.00',
   srate : '0.00',
   color : 'red',
   ...
   ...
}
Run Code Online (Sandbox Code Playgroud)

现在我被要求将上传文件功能添加到我的表单中,当然这是不可能通过JSON,因此我打算转移到FormData.那么有什么方法可以将我的JS对象转换为FormData

ade*_*neo 127

如果您有一个对象,则可以轻松创建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
});
Run Code Online (Sandbox Code Playgroud)

MDN文档中有更多示例

  • @Lior你打算下次用稻草制造飞机,希望能吸引更多真正的飞机从天而降食物吗?重要的是要理解_why_使用hasOwnProperty检查,只是说事情被认为是"最佳实践",因为你读某人(猜测,Crockford's)的书不会让你走得太远,试图教育一个同胞So成员超过100次声誉和你所拥有的答案数量的100倍也无助于你的观点.另外,名称_one_新的第三方库改变原型?那个帖子来自不同的时间...... (5认同)
  • @Lior - "item"是由OP创建的常规对象,所以它不应该有任何不属于它的属性,除非有人犯了将对象构造函数原型化的错误,在这种情况下你会在麻烦的世界,这不是我们应该保护的东西. (3认同)
  • @Lior - 如果您的服务器在POST请求中收到另一个键/值对时断开,那么您做错了.我认为答案很好,我不打算将它改为使用`Object.keys`或`hasOwnProperty()`,因为对象已发布在问题中,不应该需要其中任何一个.你有时在插件中看到`hasOwnProperty`的原因是因为你永远不知道某些人可能会对`Object`构造函数做些什么,但是大多数人不应该测试对象上的继承属性.创建,这表明你可能做错了什么. (3认同)
  • 将复杂的 json 对象(包含嵌套对象和数组)转换为 FormData 对象的方法是什么? (3认同)
  • @Lior - 它只是将键/值对添加到FormData,添加一个原型属性不会破坏任何东西,使用`Object.keys`不是答案,因为你不应该把键作为一个数组,然后迭代通过键来获取值,你应该使用`for..in`循环. (2认同)
  • 当然它会,你不知道服务器期待什么...因为...在JS中是problamtic,解决方案不一定是Object.keys(),它可能是hasOwnProperty(),但是它至少需要一个警告. (2认同)
  • 我不同意一些第三方库改变原生原型,如果你给出答案,你应该警告潜在的陷阱.但没关系,这不会去任何地方...... ps http://stackoverflow.com/questions/684672/loop-through-javascript-object (2认同)
  • @BenjaminGruenbaum你们两个都错过了我的观点,出于某种原因,您反而对整个事情感到非常激动(至少adeneo保持冷静和专业)。没有试图教育任何人的想法(尽管我认为即使您拥有“声誉的100倍以上,答案的数量100倍”,也无法向某人学习的想法是荒谬的……尤其是当SO完全关于学习)。 (2认同)

Jac*_*zen 63

使用ES6和更多功能编程方法@ adeneo的答案可能如下所示:

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

或者使用.reduce()和箭头功能:

getFormData = object => Object.keys(object).reduce((formData, key) => {
    formData.append(key, object[key]);
    return formData;
}, new FormData());
Run Code Online (Sandbox Code Playgroud)

  • 我刚刚对你的两个答案进行了性能比较,其中“.reduce()”的速度快了 1-2%。https://jsbench.me/m3kg87fxvt/1 (2认同)
  • 此外,使用“Object.entries”在这里会更实用,因为“object”是“object[key]”中的外部函数调用。 (2认同)
  • 这不是递归的 (2认同)

Vla*_*hin 32

此函数将对象的所有数据添加到FormData

来自@ developer033的ES6版本:

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)
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

  • ES6版本(没有任何lib):https://plnkr.co/edit/24fsVLGn6rLEPhRDM0R7. (5认同)

井上智*_*上智文 14

function toFormData(o) {
  return Object.entries(o).reduce((d,e) => (d.append(...e),d), new FormData())
}

var object = {
  username: 'JohnDoe',
  file: new File(['foo'], 'foo.txt', {type: 'text/plain'})
}

fetch('https://httpbin.org/post', {
  method: 'POST',
  body: toFormData(object)
}).then(r => r.json()).then(console.log)
Run Code Online (Sandbox Code Playgroud)


Gud*_*ain 10

其他答案对我来说不完整.我从@Vladimir Novopashin回答并修改了它.以下是我需要的东西以及我发现的错误:

  • 支持文件
  • 支持数组
  • 错误:需要添加复杂对象内的文件.prop而不是[prop].例如,formData.append('photos[0][file]', file)没有对谷歌浏览器工作,而 formData.append('photos[0].file', file)工作
  • 忽略我对象中的一些属性

以下代码应适用于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;
}
Run Code Online (Sandbox Code Playgroud)

  • 唯一支持数组、对象和文​​件的答案。 (3认同)
  • 我正在尝试使用它,但没有使用示例。ignoreList 参数的预期格式会非常有帮助。 (2认同)
  • 这段代码(尤其是参数)的一些插图会非常有帮助。 (2认同)

Ben*_*arp 9

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
}
Run Code Online (Sandbox Code Playgroud)

类型

type FormDataPrimitive = string | Blob | number | boolean

interface FormDataNest {
  [x: string]: FormVal
}

type FormVal = FormDataNest | FormDataPrimitive
Run Code Online (Sandbox Code Playgroud)

  • 我喜欢这个解决方案,但我建议从 `if (isNil(data)) return` 开始,因为 `typeof null === 'object'`。 (2认同)
  • 很棒的解决方案,我建议将数组部分更改为:```let obj: FormDataNest = {}; obj[索引] = el; buildFormData(formData, obj,parentKey)``` 因为您也需要在数组中附加索引,而不是压平它。 (2认同)

Raj*_*dal 7

我有一个场景,在构建表单数据时,必须以线性方式序列化嵌套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"
   }
}
Run Code Online (Sandbox Code Playgroud)

进入这样的事情:

{
   "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"
}
Run Code Online (Sandbox Code Playgroud)

服务器将接受采用此转换格式的表单数据.

这是功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

调用:

        var testJSON = {};
        var form_data = jsonToFormData (jsonForPost, testJSON);
Run Code Online (Sandbox Code Playgroud)

我正在使用testJSON来查看转换后的结果,因为我无法提取form_data的内容.AJAX发帖:

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


Nel*_*ing 6

这是一个简短而甜蜜的解决方案,使用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
Run Code Online (Sandbox Code Playgroud)

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

  • 这不适用于嵌套对象 (2认同)

Ale*_*rus 5

很抱歉回答晚了,但我一直在努力解决这个问题,因为 Angular 2 目前不支持文件上传。因此,方法是发送一个XMLHttpRequestwith FormData。所以,我创建了一个函数来完成它。我正在使用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);
        }
    }
Run Code Online (Sandbox Code Playgroud)


Uda*_*man 5

尝试如下的JSON.stringify函数

var postData = JSON.stringify(item);
var formData = new FormData();
formData.append("postData",postData );
Run Code Online (Sandbox Code Playgroud)

  • 不适用于File和Date对象。 (2认同)

Ahs*_*ooq 5

您可以简单地使用:

formData.append('item', JSON.stringify(item));
Run Code Online (Sandbox Code Playgroud)