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 객체에 추가 할 수 있습니다.
adeneo

FormData가 의미하는 바를 보여줄 수 있습니까? 특정 형식?
Sunny Sharma 2014

답변:


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- itemOP에 의해 생성 된 일반 객체이므로 누군가가 Object 생성자에 무언가를 프로토 타이핑하는 실수를하지 않는 한 자신의 소유가 아닌 속성을 가져서는 안됩니다.이 경우 문제가 발생할 수 있습니다. , 그리고 우리가 보호해야 할 문제가 아닙니다.
adeneo 2014 년

2
@Lior-키 / 값 쌍을 FormData에 추가하고 프로토 타입 속성을 추가하면 아무것도 깨지지 않으며 Object.keys키를 배열로 가져올 필요가 없으므로 키를 사용하는 것이 답이 아닙니다. 값, for..in루프 를 사용해야합니다 .
adeneo

2
물론 그것은 서버가 무엇을 기대하고 있는지 알 수 없습니다 ... JS에서 선언적이며 솔루션은 Object.keys () 일 필요는 없으며 hasOwnProperty () 일 수 있지만 최소한 경고가되어야합니다.
Lior

3
@Lior-POST 요청에서 하나 이상의 키 / 값 쌍을 수신 할 때 서버가 중단되면 잘못된 것입니다. 나는 대답이 괜찮다고 생각하고 그것을 사용 Object.keys하거나 hasOwnProperty()객체가 질문에 게시되어 있으므로 그것을 변경하지 않을 것 입니다. hasOwnProperty플러그인 등에서 가끔 사용되는 이유 는 어떤 사람들이 Object생성자에 대해 무엇을 할 수 있는지 알지 못하기 때문입니다. 하지만 대부분의 경우 사람들은 자신이 생성 한 객체에서 상속 된 속성을 테스트 할 필요가 없습니다. 아마도 뭔가 잘못하고있을 것입니다.
adeneo

5
@Lior 하늘에서 음식을 떨어 뜨리는 실제 비행기를 더 많이 끌어들이 길 바라면서 다음에는 짚으로 비행기를 만들 계획이신가요? hasOwnProperty 검사가 사용되는 이유 를 이해 하는 것이 중요합니다. 다른 사람의 (추측, Crockford의) 책을 읽는 것이 100 번 이상 So 회원을 교육하려고하는 것은 그리 멀지 않기 때문입니다. 평판과 답변의 100 배는 당신의 요점에 그다지 도움이되지 않습니다. 또한, 이름을 하나의 프로토 타입을 변경 새로운 타사 lib 디렉토리? 그 게시물은 다른 시간에서 온 것입니다 ...
Benjamin Gruenbaum

84

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

니스 그것을 유지
비벡 트레이 도시

아주 잘 작동합니다! 감사합니다! 또한 추가했다 && !(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

나는 이것을 사용하려고하는데 사용 예가 없습니다. ignoreList 매개 변수의 예상 형식이 매우 유용합니다.
jpro

8

아래와 같이 JSON.stringify 기능을 사용해보십시오

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

1
이것이이를 달성하는 가장 좋은 방법입니다.
Rob

그것의 유지는 오류 디버그의 여러 번 후에 json을 추가합니다
Snow Bases

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

form_data의 내용을 추출 할 수 없기 때문에 변환 된 결과를보기 위해 testJSON을 사용하고 있습니다. AJAX 사후 통화 :

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

안녕 Raj, 배열은 어때? 청구서 수신 주소가 2 개 이상이라고 가정합니다. 어떻게 고칠까요?
Sam

1
답을 찾았습니다! 귀하의 게시물은 정말 도움이됩니다. 감사!
Sam

3

늦은 답변으로 죄송하지만 Angular 2는 현재 파일 업로드를 지원하지 않기 때문에 어려움을 겪고 있습니다. 그래서 그것을하는 방법 XMLHttpRequestFormData. 그래서 그것을하기위한 함수를 만들었습니다. 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
먼저 ( typescriptlang.org/docs/handbook/namespaces.htmlgithub.com/Microsoft/TypeScript/issues/… ) namespace에 예약 된 키워드입니다 . 또한, 당신이 처리하는 것을 잊었다 것으로 보인다 마지막으로 이후 의 의지 받는 . TypeScriptFileelseappend "[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
ling

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

자바 스크립트 객체를 수신하고 여기에서 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

나는 이것을 Gudradain의 대답 에서 참조합니다 . 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;
}

어떻게 작동합니까? 무시 목록 (두 번째 인수. 누군가가 도움이 될 더 나은 방법을 결정할 수 있다면!)을 사용하여 json 문자열로 설정 한 File 객체를 예상하는 모든 속성을 변환하고 반환합니다.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를 처음 사용하므로 피드백을 높이 평가할 것입니다!


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.