dołączanie tablicy do FormData i wysyłanie przez AJAX

109

Używam AJAX do przesłania formularza wieloczęściowego z tablicą, polami tekstowymi i plikami.

W ten sposób dołączam każdą VAR do głównych danych

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

następnie używam funkcji ajax, aby wysłać ją do pliku PHP do przechowywania w sql DB.

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

Ale po stronie PHP arrzmienna, która jest tablicą, pojawia się jako ciąg.

Kiedy nie wysyłam go z AJAX jako danymi formularza, ale używam prostej $.POSTopcji, otrzymuję go jako tablicę po stronie PHP, ale nie mogę również wysłać plików.

jakieś rozwiązania?

shultz
źródło

Odpowiedzi:

93

Masz kilka możliwości:

Przekonwertuj go na ciąg JSON, a następnie przeanalizuj w PHP (zalecane)

JS

var json_arr = JSON.stringify(arr);

PHP

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

Lub użyj metody @ Curios

Wysyłanie tablicy za pośrednictwem FormData.


Niezalecane: Serializacja danych za pomocą, a następnie deserializacja w PHP

JS

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

PHP

$arr = explode("<#>", $_POST['arr']);
Richard de Wit
źródło
1
problem polega na tym, że tablica zawiera wiersze PRAWDZIWEGO tekstu ze spacjami i znakami interpunkcyjnymi. Nie chcę tego zepsuć.
shultz
3
Kiedy kodujesz i analizujesz go za pomocą JSON, dane nie są tracone. Spróbuj;)
Richard de Wit
Jeśli używasz asp.net z automatycznym mapowaniem lub czymś podobnym, to @Curious answer jest tym, czego potrzebujesz.
Martín Coll,
1
@Richard de Wit Jeśli masz dane takie jak File lub FormData, utracisz je w json.stringfy
Mohsen
Lubię bardziej strunowe, prostsze. Ponieważ musisz wykonać jakąś rekursję, aby przekazać tablicę tablic przy użyciu [], ale dobrze wiedzieć, że można to zrobić w ten sposób.
Chopnut
260

Możesz również wysłać tablicę w FormDataten sposób:

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

Możesz więc pisać arr[]tak samo, jak robisz to za pomocą prostego formularza HTML. W przypadku PHP powinno działać.

Ten artykuł może okazać się przydatny: Jak przekazać tablicę w ciągu zapytania?

Oleg
źródło
1
@Oleg Co jest potrzeba do napisania arr[]w formData.append('arr[]', arr[i]);? dlaczego nie jest arrpoprawne? Próbowałem obu, ale tylko arr[]działało.
Totoro
@Totoro, ponieważ w przypadku, gdy po arrprostu przedefiniuj tę wartość w każdej iteracji pętli, a na końcu końcowa wartość będzie równa ostatniemu elementowi tablicy, ale nie całej tablicy
Oleg
@Oleg Jeśli chodzi o przedefiniowanie, to co jest innego arr[], dlaczego nie jest arr[]przedefiniowane? arr[]jest również ciągiem. A podczas testowania ani, arrani nie arr[]zostało w moim przypadku przedefiniowane. Otrzymałem wiele tablic w FormData z tym samym kluczem, ale inną wartością. Więc mam arrwartość, 1a drugą arrwartość 2.
Totoro
@Totoro tak, masz rację, mój błąd. Uważam, że jest to bardziej specyficzne dla serwera pytanie. Różne języki mogą różnie analizować ciąg zapytania. Na przykład PHP zachowuje się tak, jak opisałeś, ale widziałem przykłady (jeśli pamięć służy, w Javie), w których arrdziałało również dla tablic. W tym temacie jest bardziej szczegółowa odpowiedź na to pytanie
Oleg
Jeśli ktoś chce opublikować tablicę obiektów, możesz rozszerzyć tę odpowiedź w następujący sposóbfor (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }
edqwerty
7

To jest stare pytanie, ale ostatnio napotkałem ten problem z publikowaniem obiektów wraz z plikami. Musiałem mieć możliwość opublikowania obiektu z właściwościami potomnymi, które były również obiektami i tablicami.

Poniższa funkcja przejdzie przez obiekt i utworzy prawidłowy obiekt formData.

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

Spowoduje to konwersję następującego pliku json -

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

do następującego FormData

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader
VtoCorleone
źródło
Było to dla mnie przydatne, po prostu musiałem zastosować String (wartość) na value wewnątrz append (w przeciwnym razie nie powiedzie się dla prawda / fałsz). Powinien też być (value !== null) && formData.append(key, value)zamiast po prostu w formData.append(key, value)przeciwnym razie nie działa przy wartościach zerowych
Alexander
7

Wersja maszynopisu:

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

Za pomocą:

let formData = Utility.convertModelToFormData(model);
Mohammad Dayyan
źródło
świetna robota, super przydatna: D
Cosimo Chellini
3

dodaj wszystkie typy danych wejściowych do 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
źródło
2

Jeśli masz zagnieżdżone obiekty i tablice, najlepszym sposobem na wypełnienie obiektu FormData jest użycie rekursji.

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
źródło
1

Następna wersja obowiązuje dla modelu zawierającego tablice prostych wartości:

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;
}
Megabajt
źródło
1

W oparciu o krótszą wersję rekurencyjną @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);
    }
}

Przykład użycia:

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

Wysłane dane:

data[a]=1&
data[b]=2&
data[c][d]=3
dikirill
źródło