JS-Objekt in Formulardaten konvertieren


127

Wie kann ich mein JS-Objekt in konvertieren FormData?

Der Grund, warum ich dies tun möchte, ist, dass ich ein Objekt habe, das ich aus den ~ 100 Formularfeldwerten konstruiert habe.

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

Jetzt werde ich gebeten, die Funktion zum Hochladen von Dateien zu meinem Formular hinzuzufügen, was über JSON natürlich nicht möglich ist, und daher plane ich, zu zu wechseln FormData. Gibt es eine Möglichkeit, mein JS-Objekt zu konvertieren FormData?


Kannst du deine Arbeit / deinen Fortschritt teilen?
Ritikesh

Wie wäre es mit JSON.stringify ()?
Sunny Sharma

1
@Sunny - Dadurch wird ein JSON-Text in einer Zeichenfolge erstellt. Das ist kein FormDataObjekt.
Quentin

Ja, Sie können an formData-Objekte anhängen.
Adeneo

Können Sie uns zeigen, was Sie mit FormData meinen? irgendein bestimmtes Format?
Sunny Sharma

Antworten:


153

Wenn Sie ein Objekt haben, können Sie einfach ein FormData-Objekt erstellen und die Namen und Werte dieses Objekts an formData anhängen.

Sie haben keinen Code veröffentlicht, daher handelt es sich um ein allgemeines Beispiel.

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

Weitere Beispiele finden Sie in der Dokumentation zu MDN


3
@Lior - itemist ein reguläres Objekt, das vom OP erstellt wurde, daher sollte es keine Eigenschaften haben, die nicht seine eigenen sind, es sei denn, jemand hat den Fehler gemacht, etwas auf dem Objektkonstruktor zu prototypisieren. In diesem Fall wären Sie in einer Welt voller Probleme und davor sollten wir uns nicht schützen müssen.
Adeneo

2
@Lior - Es wird lediglich die Schlüssel / Wert-Paare zu FormData hinzugefügt, eine prototypisierte Eigenschaft wird nicht beschädigt, und die Verwendung Object.keysist nicht die Antwort, da Sie die Schlüssel nicht als Array abrufen und dann über die Schlüssel iterieren müssen, um sie abzurufen Für die Werte sollten Sie eine for..inSchleife verwenden.
Adeneo

2
Natürlich wird es so sein, Sie wissen nicht, was der Server erwartet ... denn ... in JS ist problematisch, die Lösung muss nicht Object.keys () sein, es könnte hasOwnProperty () sein, aber Es muss mindestens eine Warnung sein.
Lior

3
@Lior - Wenn Ihr Server ausfällt, wenn er in einer POST-Anforderung ein weiteres Schlüssel / Wert-Paar empfängt, machen Sie es falsch. Ich denke, die Antwort ist in Ordnung, und ich werde sie nicht ändern, um sie zu verwenden, Object.keysoder hasOwnProperty()da das Objekt in der Frage veröffentlicht ist und keine davon benötigen sollte. Der Grund, den Sie manchmal hasOwnPropertyin Plugins usw. sehen, ist, dass Sie nie wissen, was manche Leute mit dem ObjectKonstruktor machen könnten , aber zum größten Teil sollten die Leute nicht auf geerbte Eigenschaften von Objekten testen müssen, die sie erstellt haben, das ist ein Zeichen dafür Du machst wahrscheinlich etwas falsch.
Adeneo

5
@Lior haben Sie vor, als nächstes Flugzeuge aus Stroh zu bauen, in der Hoffnung, dass dies mehr echte Flugzeuge anzieht, die Lebensmittel vom Himmel fallen lassen? Es ist wichtig zu verstehen, warum ein hasOwnProperty-Check verwendet wird. Wenn Sie nur sagen, dass Dinge als "Best Practice" gelten, weil Sie das Buch von jemandem (Vermutung, Crockfords) lesen, kommen Sie nicht weit und versuchen, ein So-Mitglied mehr als 100 Mal zu erziehen Der Ruf und die 100-fache Anzahl der Antworten, die Sie haben, helfen Ihrem Standpunkt auch nicht sehr. Nennen Sie auch eine neue Drittanbieter-Bibliothek, die den Prototyp ändert? Dieser Beitrag ist aus einer anderen Zeit ...
Benjamin Gruenbaum

83

Mit ES6 und einem funktionaleren Programmieransatz könnte die Antwort von @ adeneo folgendermaßen aussehen:

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

Und alternativ mit .reduce()und Pfeilfunktionen:

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

43

Diese Funktion fügt alle Daten vom Objekt zu FormData hinzu

ES6-Version von @ 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)

jQuery-Version:

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

Schön weiter so
Vivek Doshi

Funktioniert sehr gut! Danke dir! Ich musste auch && !(data instanceof Blob)in meinem Fall hinzufügen , um meine Bilder hochzuladen
Clément Baconnier

Funktioniert gut für mich, ich habe if (typeof data === 'object' && data! == null) {hinzugefügt, weil es eine Ausnahme
auslöste,

Für die ES6-Version habe ich && !(Array.isArray(data) && !data.length)in der "if" -Bedingung hinzugefügt , oder leeres Array würde entfernt.
Mtxz

14

Die anderen Antworten waren für mich unvollständig. Ich habe mit der Antwort von @Vladimir Novopashin begonnen und sie geändert. Hier sind die Dinge, die ich brauchte und einen Fehler, den ich gefunden habe:

  • Unterstützung für Datei
  • Unterstützung für Array
  • Fehler: Datei innerhalb eines komplexen Objekts muss mit .propstatt hinzugefügt werden [prop]. Zum Beispiel formData.append('photos[0][file]', file)funktionierte nicht auf Google Chrome, während formData.append('photos[0].file', file)gearbeitet
  • Ignoriere einige Eigenschaften in meinem Objekt

Der folgende Code sollte in IE11- und immergrünen Browsern funktionieren.

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
Die einzige Antwort, die Arrays, Objekte und Dateien unterstützt.
SOTN

Hallo, warum fügst du die Datei zum Stammverzeichnis hinzu? Ist es möglich, es auch dem Kind hinzuzufügen?
Cedric Arnould

@CedricArnould Es könnte ein Missverständnis sein, aber die Methode ist rekursiv. Selbst wenn sie geschrieben ist formData.append(root, data), bedeutet dies nicht, dass sie dem Stamm hinzugefügt wird.
Gudradain

Ich verstehe Ihre Antwort. Seltsamerweise habe ich eine eindeutige Sammlung von Dateien und Daten, wenn ich das Ergebnis auf dem Server erhalte. Aber ich kann in einer Datei sehen, dass der Name die Information gibt, mit welchem ​​Kind es verbunden ist. Möglicherweise liegt das Problem an .Net Core und an der Verwaltung von Upload-Dateien. Danke für deine Antwort.
Cedric Arnould

Ich versuche dies zu verwenden, aber es gibt kein Anwendungsbeispiel. Das erwartete Format des Parameters ignoreList wäre ziemlich hilfreich.
jpro

8

Versuchen Sie die Funktion JSON.stringify wie folgt

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

1
Dies ist der beste Weg, um dies zu erreichen.
Rob

Es wird der JSON nach mehreren Fehlern beim Debuggen angehängt
Snow Bases

7

Ich hatte ein Szenario, in dem verschachteltes JSON linear serialisiert werden musste, während Formulardaten erstellt wurden, da der Server auf diese Weise Werte erwartet. Also habe ich eine kleine rekursive Funktion geschrieben, die den JSON wie folgt übersetzt:

{
   "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 so etwas:

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

Der Server würde Formulardaten akzeptieren, die in diesem konvertierten Format vorliegen.

Hier ist die Funktion:

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

Aufruf:

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

Ich verwende testJSON nur, um die konvertierten Ergebnisse anzuzeigen, da ich den Inhalt von form_data nicht extrahieren kann. AJAX Post Call:

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

Hallo Raj, wie wäre es mit Arrays? Angenommen, Sie haben mehr als 1 Rechnungsadresse. Wie würden Sie das beheben?
Sam

1
Ich habe die Antwort gefunden! Ihr Beitrag ist wirklich hilfreich. Vielen Dank!
Sam

3

Entschuldigen Sie die späte Antwort, aber ich hatte Probleme damit, da Angular 2 das Hochladen von Dateien derzeit nicht unterstützt. Der Weg, dies zu tun, war das Senden eines XMLHttpRequestmit FormData. Also habe ich eine Funktion dafür erstellt. Ich benutze Typescript . Um es in Javascript zu konvertieren, entfernen Sie einfach die Datentypdeklaration.

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

Sie müssen Array-Indizes anstelle von []for-Objekteigenschaften in ein numerisches Array aufnehmen, um intakt zu bleiben
Vicary

1

TypeScript-Version:

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
Zunächst namespaceist ein reserviertes Schlüsselwort in TypeScript( typescriptlang.org/docs/handbook/namespaces.html und github.com/Microsoft/TypeScript/issues/… ). Es scheint auch, dass Sie vergessen haben, sich Fileseit dem letzten elseWillen append "[object File]"mit dem zu befassen formData.
Jyrkka

1

Sie können einfach installieren qs:

npm i qs

Einfach importieren:

import qs from 'qs'

Objekt übergeben an qs.stringify():

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

qs.stringify(item)

1

Rekursiv

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>


beste Antwort imho
ling

0

Diese Methode konvertiert ein JS-Objekt in 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]));
Korrigieren Sie

0

In meinem Fall hatte mein Objekt auch eine Eigenschaft, die ein Array von Dateien war. Da sie binär sind, sollten sie anders behandelt werden - der Index muss nicht Teil des Schlüssels sein. Also habe ich die Antwort von @Vladimir Novopashin und @ developer033 geändert:

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

Ich habe dies verwendet, um meine Objektdaten als Formulardaten zu veröffentlichen.

const encodeData = require('querystring');

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

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

0

Vielleicht suchen Sie nach diesem Code, der Ihr Javascript-Objekt empfängt, daraus ein FormData- Objekt erstellt und es dann mithilfe der neuen Fetch-API auf Ihren Server überträgt :

    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

Ich beziehe mich auf Gudradains Antwort . Ich bearbeite es ein wenig im Typescript-Format.

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

Ich bin vielleicht etwas spät dran, aber das habe ich erstellt, um ein einzelnes Objekt in FormData zu konvertieren.

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

Wie funktioniert es? Es werden alle Eigenschaften konvertiert und zurückgegeben, die Dateiobjekte erwarten, die Sie in der Ignorierliste festgelegt haben (2. Argument. Wenn mir jemand einen besseren Weg nennen könnte, dies zu bestimmen, der helfen würde!), In eine JSON-Zeichenfolge mit JSON.stringify. Dann müssen Sie es auf Ihrem Server nur noch zurück in ein JSON-Objekt konvertieren.

Beispiel:

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

Ich bin noch ein bisschen neu in HTTP-Anfragen und JavaScript, daher wäre jedes Feedback sehr dankbar!


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.