anexar array a FormData e enviar via AJAX


108

Estou usando ajax para enviar um formulário multiparte com array, campos de texto e arquivos.

Anexo cada VAR aos dados principais, assim

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

então eu uso a função ajax para enviá-lo a um arquivo PHP para armazenar dentro do banco de dados sql.

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

Mas no lado do PHP, a arrvariável, que é um array, aparece como uma string.

Quando eu não o envio com ajax como dados de formulário, mas uso a $.POSTopção simples, eu o obtenho como um array no lado do PHP, mas não consigo enviar os arquivos também.

alguma solução?

Respostas:


92

Você tem várias opções:

Converta-o em uma string JSON e analise-o em PHP (recomendado)

JS

var json_arr = JSON.stringify(arr);

PHP

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

Ou use o método de @Curios

Enviando uma matriz por FormData.


Não recomendado: serialize os dados com e depois desserialize em PHP

JS

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

PHP

$arr = explode("<#>", $_POST['arr']);

1
o problema é que o array contém linhas de texto REAL, com espaços e sinais de pontuação. Eu não quero bagunçar tudo.
— shultz de

3
Quando você o codifica e analisa com JSON, os dados não são perdidos. Experimente;)
— Richard de Wit

Se você estiver usando asp.net com mapeamento automático ou algo semelhante, a resposta @Curious é o que você precisa.
— Martín Coll

1
@Richard de Wit Se você tiver dados como Arquivo ou FormData, você os perderá em json.stringfy
— Mohsen

Gosto mais de stringified, mais simples. Como você precisa fazer algum tipo de recursão para passar array de arrays usando [], mas é bom saber que pode ser feito dessa forma.
— Chopnut de

260

Você também pode enviar uma matriz FormDatadesta forma:

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

Portanto, você pode escrever arr[]da mesma maneira que faria com um formulário HTML simples. No caso do PHP, deve funcionar.

Você pode achar este artigo útil: Como passar uma matriz em uma string de consulta?


1
@Oleg Qual é a necessidade de escrever arr[]em formData.append('arr[]', arr[i]);? porque não está arrcorreto? Tentei os dois, mas só arr[]funcionou.
— Totoro

@Totoro porque no caso de arrvocê apenas redefinir este valor em cada iteração do loop, e no final, o valor final seria igual ao último elemento da matriz, mas não a matriz inteira
— Oleg

@Oleg Se redefinir é o caso, então o que é diferente em arr[], por que não é arr[]redefinido? arr[]também é uma string. E ao testar ambos nem arrnem arr[]foi redefinido no meu caso. Eu tenho vários array em FormData com a mesma chave, mas com valor diferente. Então eu peguei arrcom valor 1e outra arrcom valor 2.
— Totoro

@Totoro sim, você está certo, erro meu. Eu acredito que esta é uma questão mais específica do servidor. Idiomas diferentes podem analisar string de consulta de maneira diferente. Por exemplo, o PHP se comporta como você descreveu, mas eu vi exemplos (se a memória servir, em Java), onde arrtambém funcionou para arrays. Em este tema há uma resposta mais detalhada a esta pergunta
— Oleg

Se alguém quiser postar uma série de objetos, você pode estender esta resposta da seguinte maneirafor (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 é uma questão antiga, mas recentemente encontrei este problema ao postar objetos junto com arquivos. Eu precisava ser capaz de postar um objeto, com propriedades filho que eram objetos e matrizes também.

A função abaixo irá percorrer um objeto e criar o objeto formData correto.

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

Isso converterá o seguinte json -

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

no seguinte FormData

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader

Foi útil para mim, apenas tive que aplicar String (valor) no valor dentro de append (caso contrário, falha para verdadeiro / falso). Além disso, deveria ser em (value !== null) && formData.append(key, value)vez de apenas formData.append(key, value)falhar em valores nulos
— Alexander

7

Versão datilografada:

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

Usando:

let formData = Utility.convertModelToFormData(model);

ótimo trabalho, super útil: D
— Cosimo Chellini

2

adicione todas as 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

Se você tiver objetos e matrizes aninhados, a melhor maneira de preencher o objeto FormData é usando recursão.

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 versão válida para modelo contendo matrizes 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

Com base na versão de recursão mais curta da resposta @YackY:

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

Exemplo de uso:

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

Dados enviados:

data[a]=1&
data[b]=2&
data[c][d]=3
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.