Converti oggetto JS in dati del modulo


128

Come posso convertire il mio oggetto JS in FormData?

Il motivo per cui voglio farlo è che ho un oggetto che ho costruito con i valori del campo del modulo ~ 100.

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

Ora mi viene chiesto di aggiungere la funzionalità di caricamento del file al mio modulo che, ovviamente, è impossibile tramite JSON e quindi ho intenzione di trasferirmi FormData. Quindi c'è un modo in cui posso convertire il mio oggetto JS FormData?


puoi condividere il tuo lavoro / progresso?
Ritikesh

che ne dici di JSON.stringify ()?
Sunny Sharma

1
@Sunny - Questo produrrà un testo JSON in una stringa. Questo non è un FormDataoggetto.
Quentin

Sì, puoi aggiungere agli oggetti formData.
adeneo

puoi mostrarci cosa intendi per FormData? qualche formato specifico?
Sunny Sharma

Risposte:


153

Se si dispone di un oggetto, è possibile creare facilmente un oggetto FormData e aggiungere i nomi ei valori di tale oggetto a formData.

Non hai pubblicato alcun codice, quindi è un esempio generale;

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

Ci sono altri esempi nella documentazione su MDN


3
@Lior - itemè un normale oggetto creato dall'OP, quindi non dovrebbe avere proprietà che non siano di sua proprietà, a meno che qualcuno non abbia commesso l'errore di prototipare qualcosa sul costruttore Object, nel qual caso saresti in un mondo di guai e non è qualcosa da cui dovremmo proteggerci.
adeneo

2
@Lior - sta solo aggiungendo le coppie chiave / valore a FormData, l'aggiunta di una proprietà prototipata non interromperà nulla e l'uso Object.keysnon è la risposta, poiché non dovresti dover ottenere le chiavi come array, quindi iterare sulle chiavi per ottenere i valori, dovresti usare un for..inciclo.
adeneo

2
Certo che lo farà, non sai cosa si aspetta il server ... perché ... in JS è problamtic, la soluzione non deve essere Object.keys (), potrebbe essere hasOwnProperty (), ma deve essere almeno un avvertimento.
Lior

3
@Lior - Se il tuo server si interrompe quando riceve un'altra coppia chiave / valore in una richiesta POST, stai sbagliando. Penso che la risposta vada bene e non la cambierò per usarla Object.keyso hasOwnProperty()poiché l'oggetto è pubblicato nella domanda e non dovrebbe aver bisogno di nessuno di questi. Il motivo per cui a volte vedi hasOwnPropertyusato nei plugin ecc. È perché non sai mai cosa potrebbero fare alcune persone al Objectcostruttore, ma per la maggior parte le persone non dovrebbero dover testare le proprietà ereditate sugli oggetti che hanno creato, questo è un segno che probabilmente stai facendo qualcosa di sbagliato.
adeneo

5
@ Lior, la prossima volta intendi costruire aeroplani di paglia, sperando che attirino più aeroplani reali che lasceranno cadere cibo dal cielo? È importante capire perché viene utilizzato un controllo hasOwnProperty, semplicemente dicendo che le cose sono considerate "best practice" perché leggere il libro di qualcuno (supponendo, di Crockford) non ti porta molto lontano, cercando di educare un collega membro di So con più di 100 volte la reputazione e 100 volte il numero di risposte che hai non aiuta molto neanche il tuo punto di vista. Inoltre, nominare una nuova libreria di terze parti che cambia il prototipo? Quel post è di un'altra epoca ...
Benjamin Gruenbaum

84

Con ES6 e un approccio di programmazione più funzionale, la risposta di @ adeneo potrebbe assomigliare a questa:

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

E in alternativa usando .reduce()e le funzioni freccia:

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

44

Questa funzione aggiunge tutti i dati dall'oggetto a FormData

Versione ES6 da @ 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)

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

Bello Continuate così
Vivek Doshi

Funziona molto bene! Grazie! Ho anche dovuto aggiungere && !(data instanceof Blob)nel mio caso per caricare le mie immagini
Clément Baconnier

Funziona bene per me, ho aggiunto if (typeof data === 'object' && data! == null) {perché stava lanciando un'eccezione se il valore è null
al000y

Per la versione ES6, ho aggiunto && !(Array.isArray(data) && !data.length)la condizione "if" o l'array vuoto sarebbe stato rimosso.
Mtxz

15

Le altre risposte erano incomplete per me. Ho iniziato dalla risposta di @Vladimir Novopashin e l'ho modificata. Ecco le cose di cui avevo bisogno e il bug che ho trovato:

  • Supporto per file
  • Supporto per array
  • Bug: il file all'interno di un oggetto complesso deve essere aggiunto con .propinvece di [prop]. Ad esempio, formData.append('photos[0][file]', file)non ha funzionato su Google Chrome, mentre formData.append('photos[0].file', file)funzionava
  • Ignora alcune proprietà nel mio oggetto

Il codice seguente dovrebbe funzionare su IE11 e sui browser evergreen.

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

2
L'unica risposta che supporta array, oggetti e file.
entro il

Ciao, perché aggiungi il file alla radice? È possibile aggiungerlo anche a bambino?
Cedric Arnould

@CedricArnould Potrebbe essere un malinteso ma il metodo è ricorsivo quindi anche se è scritto formData.append(root, data), non significa che sia aggiunto alla radice.
Gudradain

Capisco la tua risposta, stranamente quando ottengo il risultato nel server, ho un'unica raccolta di file e dati. Ma posso vedere in un file il nome che fornisce le informazioni a quale bambino è collegato. Forse il problema deriva da .Net Core e da come gestisce i file di caricamento. Grazie per la tua risposta.
Cedric Arnould

Sto cercando di usarlo ma non ci sono esempi di utilizzo. il formato previsto del parametro ignoreList sarebbe molto utile.
jpro

9

Prova la funzione JSON.stringify come di seguito

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

1
Questo è il modo migliore per ottenere questo risultato.
Rob il

il suo mantenimento aggiunge il json dopo diverse volte di debug di errore
Snow Bases

7

Avevo uno scenario in cui il JSON annidato doveva essere serializzato in modo lineare durante la costruzione dei dati del modulo, poiché questo è il modo in cui il server si aspetta i valori. Quindi, ho scritto una piccola funzione ricorsiva che traduce il JSON che è così:

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

In qualcosa di simile:

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

Il server accetterebbe i dati del modulo che sono in questo formato convertito.

Ecco la funzione:

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

Invocazione:

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

Sto usando testJSON solo per vedere i risultati convertiti poiché non sarei in grado di estrarre il contenuto di form_data. Chiamata post AJAX:

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

Ciao Raj, che ne dici degli array? Supponi di avere più di 1 indirizzo di fatturazione. Come lo risolveresti?
Sam il

1
Ho trovato la risposta! Il tuo post è davvero utile. Grazie!
Sam il

3

Ci scusiamo per la risposta in ritardo, ma stavo lottando con questo perché Angular 2 attualmente non supporta il caricamento di file. Quindi, il modo per farlo era inviare un XMLHttpRequestcon FormData. Quindi, ho creato una funzione per farlo. Sto usando Typescript . Per convertirlo in Javascript basta rimuovere la dichiarazione dei tipi di dati.

/**
     * 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);
        }
    }

È necessario includere gli indici dell'array invece delle []proprietà dell'oggetto all'interno di un array numerico per rimanere intatti
Vicary

1

Versione 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
Prima di tutto, namespaceè una parola chiave riservata in TypeScript( typescriptlang.org/docs/handbook/namespaces.html e github.com/Microsoft/TypeScript/issues/… ). Inoltre, sembra che ti sei dimenticato di occupartene Filepoiché l'ultima elsevolontà append "[object File]"del formData.
Jyrkka

1

Puoi semplicemente installare qs:

npm i qs

Importa semplicemente:

import qs from 'qs'

Passa oggetto a qs.stringify():

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

qs.stringify(item)

1

ricorsivamente

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>


migliore risposta imho
ling

0

Questo metodo converte un oggetto JS in un 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());
}


Basta correggere la chiamata di voci di oggetti annidati di iterazione: Object.entries(value).forEach((v, k) => acc.append(`${key}[${v[0]}]`, v[1]));
heber gentilin

0

Nel mio caso anche il mio oggetto aveva una proprietà che era un array di file. Poiché sono binari, dovrebbero essere trattati in modo diverso: l'indice non deve essere parte della chiave. Quindi ho modificato la risposta di @Vladimir Novopashin e @ 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

L'ho usato per pubblicare i miei dati oggetto come dati del modulo.

const encodeData = require('querystring');

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

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

0

Forse stai cercando questo, un codice che riceve il tuo oggetto javascript, crea un oggetto FormData da esso e poi lo POST sul tuo server usando la nuova API Fetch :

    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

Mi riferisco a questo dalla risposta di Gudradain . Lo modifico un po 'in formato 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

Potrei essere un po 'in ritardo alla festa, ma questo è ciò che ho creato per convertire un oggetto singolare in 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;
}

Come funziona? Convertirà e restituirà tutte le proprietà previste dagli oggetti File che hai impostato nell'elenco ignora (2 ° argomento. Se qualcuno potesse dirmi un modo migliore per determinarlo, sarebbe d'aiuto!) In una stringa json usando JSON.stringify. Quindi sul tuo server dovrai solo riconvertirlo in un oggetto JSON.

Esempio:

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

Sono ancora un po 'nuovo per le richieste Http e JavaScript, quindi qualsiasi feedback sarebbe molto apprezzato!


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.