将JS对象转换为表单数据


128

如何将JS对象转换为 FormData

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

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

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


您可以分享您的工作/进度吗?
Ritikesh

JSON.stringify()怎么样?
Sunny Sharma 2014年

1
@Sunny —将在字符串中生成JSON文本。那不是一个FormData对象。
Quentin 2014年

是的,可以,您可以追加到formData对象。
2014年

您能告诉我们FormData是什么意思吗?任何特定格式?
Sunny Sharma

Answers:


153

如果有一个对象,则可以轻松地创建一个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的文档中还有更多示例


3
@Lior- item是由OP创建的常规对象,因此它不应具有不属于它的任何属性,除非有人犯了将某些东西原型化到Object构造函数上的错误,否则您将陷入麻烦,这不是我们应防止的事情。
2014年

2
@Lior-只是将键/值对添加到FormData,添加原型属性不会破坏任何内容,而使用Object.keys不是答案,因为您不必将键作为数组获取,然后遍历键即可获取值,则应使用for..in循环。
2014年

2
当然,您不会知道服务器的期望...因为...在JS中是有问题的,解决方案不必是Object.keys(),可以是hasOwnProperty(),但是它至少需要是一个警告。
Lior 2014年

3
@Lior-如果您的服务器在POST请求中再收到一对键/值对时发生故障,则您做错了。我认为答案很好,并且我不会将其更改为使用,Object.keys也不会将hasOwnProperty()对象发布在问题中,并且不需要任何这些。您有时看到hasOwnProperty在插件等中使用的原因是,因为您永远不知道有人会对Object构造函数做什么,但是在大多数情况下,人们不必测试所创建对象的继承属性,这表明您可能做错了什么。
2014年

5
@Lior您打算接下来用稻草建造飞机,希望能吸引更多真正的飞机将食物从空中掉下来吗?重要的是要理解为什么使用hasOwnProperty检查,只是说事情被认为是“最佳实践”,因为您读某人的书(猜测,Crockford的书)并不会使您走得太远,试图教育一个So成员超过100次声誉和答案数量的100倍对您的观点也无济于事。另外,命名一个新的第三方库来更改原型吗?该帖子来自不同的时间……
本杰明·格林鲍姆

83

使用ES6和更实用的编程方法,@ adeneo的答案可能看起来像这样:

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

以及使用.reduce()和箭头功能:

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

44

此函数将所有数据从对象添加到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)

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);

保持良好
Vivek Doshi

效果很好!谢谢!我也不得不加&& !(data instanceof Blob)在我的情况下上传我的图片
克莱门特Baconnier

对我来说效果很好,我添加了if(typeof data ==='object'&& data!== null){因为如果该值为null则抛出异常
al000y

对于ES6版本,我添加&& !(Array.isArray(data) && !data.length)了“ if”条件,否则将删除空数组。
Mtxz

14

其他答案对我来说并不完整。我从@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;
}

1
唯一的答案支持数组,对象和文件。
sotn

嗨,为什么将文件添加到根目录?是否也可以将其添加到孩子?
Cedric Arnould

@CedricArnould可能是一个误解,但是该方法是递归的,因此即使已编写该方法formData.append(root, data),也不意味着它已添加到根目录中。
Gudradain

我理解您的回答,奇怪的是,当我在服务器中获得结果时,我具有唯一的文件和数据集合。但是我可以在文件中看到名称,该信息提供了该文件所连接的子项的信息。也许问题出在.Net Core以及它如何管理上传文件。感谢您的回答。
Cedric Arnould '18

我正在尝试使用此功能,但没有用法示例。ignoreList参数的预期格式将非常有帮助。
jpro


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"
   }
}

变成这样:

{
   "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邮寄电话:

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

嗨,Raj,数组怎么样?假设您有1个以上的帐单邮寄地址。您将如何解决?
山姆

1
我找到了答案!您的帖子确实很有帮助。谢谢!
山姆

3

抱歉,我们无法提供上载的答案,但由于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);
        }
    }

您必须[]在数字数组中包括数组索引而不是for对象属性才能保持完整
Vicary

1

TypeScript版本:

static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
    let formData = form || new FormData();
    for (let propertyName in model) {
      if (!model.hasOwnProperty(propertyName) || model[propertyName] == undefined) continue;
      let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
      if (model[propertyName] instanceof Date) {        
        formData.append(formKey, this.dateTimeToString(model[propertyName]));
      }
      else if (model[propertyName] instanceof Array) {
        model[propertyName].forEach((element, index) => {
          if (typeof element != 'object')
            formData.append(`${formKey}[]`, element);
          else {
            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;
  }

https://gist.github.com/Mds92/091828ea857cc556db2ca0f991fee9f6


1
首先,namespace处于保留关键字TypeScripttypescriptlang.org/docs/handbook/namespaces.htmlgithub.com/Microsoft/TypeScript/issues/...)。此外,您似乎也忘记了File自上次else遗嘱以来要处理append "[object File]"的问题formData
Jyrkka

1

您可以简单地安装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)

1

递归地

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>


最佳答案imho

0

此方法将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());
}


只需修复迭代嵌套对象条目调用: Object.entries(value).forEach((v, k) => acc.append(`${key}[${v[0]}]`, v[1]));
heber gentilin

0

在我的情况下,我的对象也具有文件数组的属性。由于它们是二进制的,因此应该以不同的方式处理-索引不必是键的一部分。所以我修改了@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;
}

0

我用它来发布我的对象数据作为表单数据。

const encodeData = require('querystring');

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

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

0

也许您正在寻找的是一个代码,该代码接收您的javascript对象,从中创建一个FormData对象,然后使用新的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);
        });

0

我从古德拉丹的答案中引用了这一点。我以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();

0

我可能会晚一点,但这就是我创建的将单个对象转换为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的新手,因此,我们将不胜感激任何反馈!


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.