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

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

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

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


当前回答

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

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

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

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

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

优点:

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

缺点:

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

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

优点:

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

缺点:

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

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

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

优点:

您不需要等待上传提交 您不需要担心将所有数据追加到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;
}

使用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());

抱歉这么晚才回答,我一直在纠结这个问题,因为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);
        }
    }

我可能有点晚了,但这就是我创建的将单个对象转换为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,所以任何反馈将高度赞赏!

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

             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