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?

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

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?


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

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

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

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

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

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.