agregando matriz a FormData y enviar a través de AJAX

109

Estoy usando ajax para enviar un formulario de varias partes con matriz, campos de texto y archivos.

Añado cada VAR a los datos principales como tal

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

luego uso la función ajax para enviarlo a un archivo PHP para almacenar dentro de SQL DB.

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

Pero en el lado de PHP, la arrvariable, que es una matriz, aparece como una cadena.

Cuando no lo envío con ajax como datos de formulario, pero uso la $.POSTopción simple , lo obtengo como una matriz en el lado de PHP, pero luego no puedo enviar los archivos también.

alguna solución?

shultz
fuente

Respuestas:

93

Tienes varias opciones:

Conviértalo en una cadena JSON, luego analícelo en PHP (recomendado)

JS

var json_arr = JSON.stringify(arr);

PHP

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

O usa el método de @ Curios

Envío de una matriz a través de FormData.


No recomendado: serializar los datos con, luego deserializar en PHP

JS

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

PHP

$arr = explode("<#>", $_POST['arr']);
Richard de Wit
fuente
1
el problema es que la matriz contiene líneas de texto REAL, con espacios y signos de puntuación. No quiero estropearlo.
shultz
3
Cuando lo codifica y analiza con JSON, los datos no se pierden. Pruébelo;)
Richard de Wit
Si está utilizando asp.net con mapeo automático o algo similar, la respuesta @Curious es lo que necesita.
Martín Coll
1
@Richard de Wit Si tiene datos como File o FormData, los perderá en json.stringfy
Mohsen
Me gusta más el encordado, más simple. Como necesita hacer algún tipo de recursividad para pasar una matriz de matrices usando [] pero es bueno saber que se puede hacer de esa manera.
Chopnut
260

También puede enviar una matriz de FormDataesta manera:

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

Por lo tanto, puede escribir de arr[]la misma manera que lo hace con un formulario HTML simple. En el caso de PHP debería funcionar.

Puede encontrar útil este artículo: ¿Cómo pasar una matriz dentro de una cadena de consulta?

Oleg
fuente
1
@Oleg ¿Cuál es la necesidad de escribir arr[]en el formData.append('arr[]', arr[i]);? ¿por qué no es arrcorrecto? Probé ambos pero solo arr[]funcionó.
Totoro
@Totoro porque en caso de que arrsimplemente redefina este valor en cada iteración del ciclo, y al final, el valor final sería igual al último elemento de la matriz, pero no a la matriz completa
Oleg
@Oleg Si la redefinición es el caso, entonces, ¿qué es diferente en arr[], por qué no se arr[]redefine? arr[]también es una cuerda. Y mientras probaba tanto ni arrni arr[]se redefinió en mi caso. Obtuve una matriz múltiple en FormData con la misma clave pero un valor diferente. Entonces obtuve arrcon valor 1y otro arrcon valor 2.
Totoro
@Totoro sí, tienes razón, mi error. Creo que esta es una pregunta más específica del lado del servidor. Los diferentes idiomas pueden analizar la cadena de consulta de forma diferente. Por ejemplo, PHP se comporta como lo describiste, pero vi ejemplos (si la memoria sirve, en Java), donde arrtambién funcionó para matrices. En este tema hay una respuesta más detallada a esta pregunta
Oleg
Si alguien está buscando publicar una matriz de objetos, puede extender esta respuesta de la siguiente manerafor (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }
edqwerty
7

Esta es una pregunta antigua, pero recientemente encontré este problema al publicar objetos junto con archivos. Necesitaba poder publicar un objeto, con propiedades secundarias que también fueran objetos y matrices.

La siguiente función recorrerá un objeto y creará el objeto formData correcto.

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

Esto convertirá el siguiente json:

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

en el siguiente FormData

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader
VtoCorleone
fuente
Fue útil para mí, solo tuve que aplicar String (value) en el valor dentro de append (de lo contrario, falla para verdadero / falso). También debería ser en (value !== null) && formData.append(key, value)lugar de simplemente, de lo formData.append(key, value)contrario, falla en valores nulos
Alexander
7

Versión mecanografiada:

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

Utilizando:

let formData = Utility.convertModelToFormData(model);
Mohammad Dayyan
fuente
gran trabajo, super útil: D
Cosimo Chellini
3

agregue todas las entradas de tipo a 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
fuente
2

Si ha anidado objetos y matrices, la mejor forma de rellenar el objeto FormData es mediante la recursividad.

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
fuente
1

Próxima versión válida para modelo que contiene arays de valores 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;
}
Megabyte
fuente
1

Basado en @YackY respuesta versión de recursividad más corta:

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

Ejemplo de uso:

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

Datos enviados:

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