ajout d'un tableau à FormData et envoi via AJAX

109

J'utilise ajax pour soumettre un formulaire en plusieurs parties avec un tableau, des champs de texte et des fichiers.

J'ajoute chaque VAR aux données principales comme tel

var attachments = document.getElementById('files'); 
var data= new FormData();

for (i=0; i< attachments.files.length; i++){
    data.append('file', attachments.files[i]);
    console.log(attachments.files[i]);

    data.append ('headline', headline);
    data.append ('article', article);
    data.append ('arr', arr);
    data.append ('tag', tag);

puis j'utilise la fonction ajax pour l'envoyer dans un fichier PHP à stocker dans sql DB.

$.ajax({    
    type: "post",
    url: 'php/submittionform.php',
    cache: false,
    processData: false,
    contentType: false,
    data: data,
    success: function(request) {$('#box').html(request); }
})

Mais du côté PHP, la arrvariable, qui est un tableau, apparaît sous forme de chaîne.

Lorsque je ne l'envoie pas avec ajax en tant que données de formulaire mais que j'utilise l' $.POSToption simple , je l'obtiens en tant que tableau du côté PHP, mais je ne peux pas non plus envoyer les fichiers.

des solutions?

Shultz
la source

Réponses:

93

Vous avez plusieurs options:

Convertissez-le en chaîne JSON, puis analysez-le en PHP (recommandé)

JS

var json_arr = JSON.stringify(arr);

PHP

$arr = json_decode($_POST['arr']);

Ou utilisez la méthode de @ Curios

Envoi d'un tableau via FormData.


Non recommandé: Sérialiser les données avec, puis désérialiser en PHP

JS

// Use <#> or any other delimiter you want
var serial_arr = arr.join("<#>"); 

PHP

$arr = explode("<#>", $_POST['arr']);
Richard de Wit
la source
1
le problème est que le tableau contient des lignes de texte REAL, avec des espaces et des signes de ponctuation. Je ne veux pas tout gâcher.
shultz
3
Lorsque vous l'encodez et l'analysez avec JSON, les données ne sont pas perdues. Essayez-le;)
Richard de Wit
Si vous utilisez asp.net avec une cartographie automatique ou quelque chose de similaire, la réponse @Curious est ce dont vous avez besoin.
Martín Coll
1
@Richard de Wit Si vous avez des données telles que File ou FormData, vous les perdrez dans json.stringfy
Mohsen
J'aime mieux le stringifier, plus simple. Comme vous devez faire une sorte de récursion pour passer un tableau de tableaux en utilisant [], mais bon à savoir, cela peut être fait de cette façon.
Chopnut
260

Vous pouvez également envoyer un tableau de FormDatacette manière:

var formData = new FormData;
var arr = ['this', 'is', 'an', 'array'];
for (var i = 0; i < arr.length; i++) {
    formData.append('arr[]', arr[i]);
}

Vous pouvez donc écrire de arr[]la même manière que vous le faites avec un simple formulaire HTML. Dans le cas de PHP, cela devrait fonctionner.

Cet article peut vous être utile: Comment passer un tableau dans une chaîne de requête?

Oleg
la source
1
@Oleg Quel est le besoin d'écrire arr[]en formData.append('arr[]', arr[i]);? pourquoi n'est pas arrcorrect? J'ai essayé les deux mais seulement arr[]travaillé.
Totoro
@Totoro parce que dans le cas où arrvous redéfinissez simplement cette valeur à chaque itération de boucle, et à la fin, la valeur finale serait égale au dernier élément du tableau, mais pas à tout le tableau
Oleg
@Oleg Si la redéfinition est le cas, qu'est-ce qui est différent arr[], pourquoi n'est-elle pas arr[]redéfinie? arr[]est également une chaîne. Et tout en testant les deux ni arrn'a arr[]été redéfini dans mon cas. J'ai plusieurs tableaux dans FormData avec la même clé mais une valeur différente. Donc j'ai eu arravec valeur 1et un autre arravec valeur 2.
Totoro
@Totoro oui, tu as raison, mon erreur. Je crois que c'est une question plus spécifique au serveur. Différentes langues peuvent analyser la chaîne de requête différemment. Par exemple, PHP se comporte comme vous l'avez décrit, mais j'ai vu des exemples (si ma mémoire est bonne, en Java), où cela arrfonctionnait également pour les tableaux. Dans cette rubrique, il y a une réponse plus détaillée à cette question
Oleg
Si quelqu'un cherche à publier un tableau d'objets, vous pouvez étendre cette réponse comme suitfor (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }
edqwerty
7

C'est une vieille question, mais j'ai récemment rencontré ce problème avec la publication d'objets avec des fichiers. J'avais besoin de pouvoir publier un objet, avec des propriétés enfants qui étaient également des objets et des tableaux.

La fonction ci-dessous parcourt un objet et crée l'objet formData correct.

// formData - instance of FormData object
// data - object to post
function getFormData(formData, data, previousKey) {
  if (data instanceof Object) {
    Object.keys(data).forEach(key => {
      const value = data[key];
      if (value instanceof Object && !Array.isArray(value)) {
        return this.getFormData(formData, value, key);
      }
      if (previousKey) {
        key = `${previousKey}[${key}]`;
      }
      if (Array.isArray(value)) {
        value.forEach(val => {
          formData.append(`${key}[]`, val);
        });
      } else {
        formData.append(key, value);
      }
    });
  }
}

Cela convertira le json suivant -

{
  name: 'starwars',
  year: 1977,
  characters: {
    good: ['luke', 'leia'],
    bad: ['vader'],
  },
}

dans le FormData suivant

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader
VtoCorleone
la source
C'était utile pour moi, il fallait juste appliquer String (valeur) sur la valeur à l'intérieur de append (sinon cela échoue pour vrai / faux). Aussi, il devrait être (value !== null) && formData.append(key, value)au lieu de simplement formData.append(key, value)sinon, il échoue sur des valeurs nulles
Alexander
7

Version dactylographiée:

export class Utility {      
    public static convertModelToFormData(model: any, form: FormData = null, namespace = ''): FormData {
        let formData = form || new FormData();
        let formKey;

        for (let propertyName in model) {
            if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
            let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
            if (model[propertyName] instanceof Date)
                formData.append(formKey, model[propertyName].toISOString());
            else if (model[propertyName] instanceof Array) {
                model[propertyName].forEach((element, index) => {
                    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;
    }
}

En utilisant:

let formData = Utility.convertModelToFormData(model);
Mohammad Dayyan
la source
excellent travail, super utile: D
Cosimo Chellini
3

ajouter toutes les entrées de type à FormData

const formData = new FormData();
for (let key in form) {
    Array.isArray(form[key])
        ? form[key].forEach(value => formData.append(key + '[]', value))
        : formData.append(key, form[key]) ;
}
HamidNE
la source
2

Si vous avez des objets et des tableaux imbriqués, la meilleure façon de remplir l'objet FormData consiste à utiliser la récursivité.

function createFormData(formData, data, key) {
    if ( ( typeof data === 'object' && data !== null ) || Array.isArray(data) ) {
        for ( let i in data ) {
            if ( ( typeof data[i] === 'object' && data[i] !== null ) || Array.isArray(data[i]) ) {
                createFormData(formData, data[i], key + '[' + i + ']');
            } else {
                formData.append(key + '[' + i + ']', data[i]);
            }
        }
    } else {
        formData.append(key, data);
    }
}
YackY
la source
1

Version suivante valide pour le modèle contenant des arays de valeurs simples:

function convertModelToFormData(val, formData = new FormData(), namespace = '') {
    if((typeof val !== 'undefined') && (val !== null)) {
        if(val instanceof Date) {
            formData.append(namespace, val.toISOString());
        } else if(val instanceof Array) {
            for(let element of val) {
                convertModelToFormData(element, formData, namespace + '[]');
            }
        } else if(typeof val === 'object' && !(val instanceof File)) {
            for (let propertyName in val) {
                if(val.hasOwnProperty(propertyName)) {
                    convertModelToFormData(val[propertyName], formData, namespace ? namespace + '[' + propertyName + ']' : propertyName);
                }
            }
        } else {
            formData.append(namespace, val.toString());
        }
    }
    return formData;
}
Mégaoctet
la source
1

Basé sur la version de récursivité plus courte de @YackY answer:

function createFormData(formData, key, data) {
    if (data === Object(data) || Array.isArray(data)) {
        for (var i in data) {
            createFormData(formData, key + '[' + i + ']', data[i]);
        }
    } else {
        formData.append(key, data);
    }
}

Exemple d'utilisation:

var data = {a: '1', b: 2, c: {d: '3'}};
var formData = new FormData();
createFormData(formData, 'data', data);

Données envoyées:

data[a]=1&
data[b]=2&
data[c][d]=3
dikirill
la source