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?

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

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?


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

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

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

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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.