добавление массива в FormData и отправка через AJAX

110

Я использую ajax для отправки составной формы с массивом, текстовыми полями и файлами.

Я добавляю каждую переменную к основным данным так

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

затем я использую функцию ajax, чтобы отправить его в файл PHP для хранения внутри базы данных sql.

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

Но на стороне PHP arrпеременная, которая представляет собой массив, отображается как строка.

Когда я не отправляю его с помощью ajax как данные формы, но использую простой $.POSTвариант, я получаю его как массив на стороне PHP, но тогда я также не могу отправлять файлы.

какие решения?

Шульц
источник

Ответы:

93

У вас есть несколько вариантов:

Преобразуйте его в строку JSON, затем проанализируйте в PHP (рекомендуется)

JS

var json_arr = JSON.stringify(arr);

PHP

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

Или используйте метод @Curios

Отправка массива через FormData.


Не рекомендуется: сериализовать данные с помощью, затем десериализовать в PHP.

JS

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

PHP

$arr = explode("<#>", $_POST['arr']);
Ричард де Вит
источник
1
проблема в том, что массив содержит строки НАСТОЯЩЕГО текста с пробелами и знаками препинания. Я не хочу все испортить.
shultz
3
Когда вы кодируете и анализируете его с помощью JSON, данные не теряются. Попробуйте;)
Ричард де Вит
Если вы используете asp.net с автоматическим отображением или что-то подобное, то ответ @Curious - это то, что вам нужно.
Мартин Колл,
1
@Richard де Вит Если у вас есть такие данные файла или FormData вы потеряете их в json.stringfy
Мохсен
Мне нравится струнный лучше, проще. Поскольку вам нужно выполнить какую-то рекурсию для передачи массива массивов с помощью [], но хорошо знать, что это можно сделать таким образом.
Чопнат
261

Вы также можете отправить массив FormDataследующим образом:

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

Таким образом, вы можете писать так arr[]же, как и в простой HTML-форме. В случае PHP это должно работать.

Вы можете найти эту статью полезной: Как передать массив в строке запроса?

Олег
источник
1
@Oleg Что необходимо написать arr[]в formData.append('arr[]', arr[i]);? почему не arrправильно? Я пробовал оба, но только arr[]сработало.
Тоторо
@Totoro, потому что в случае, если arrвы просто переопределяете это значение на каждой итерации цикла, и в конце концов, конечное значение будет равно последнему элементу массива, но не всему массиву
Олег
@Oleg Если дело обстоит именно с переопределением, то в чем отличие arr[], почему не arr[]переопределено? arr[]также является строкой. И при тестировании в моем случае ни одно, arrни другое не arr[]было переопределено. У меня есть несколько массивов в FormData с тем же ключом, но с другим значением. Итак, я получил arrс ценностью, 1а другой arrс ценностью 2.
Тоторо
@Totoro да, ты прав, моя ошибка. Я считаю, что это скорее вопрос, связанный с сервером. Разные языки могут по-разному анализировать строку запроса. Например, PHP ведет себя так, как вы описали, но я видел примеры (если не изменяет память, на Java), где arrтакже работал с массивами. В этой теме есть более развернутый ответ на этот вопрос
Олег
Если кто-то хочет опубликовать массив объектов, вы можете расширить этот ответ следующим образомfor (var i = 0; i < myArr; i++) { var myItemInArr = myArr[i]; for (var prop in myItemInArr) { fileData.append(`myArr[${i}][${prop}]`, myItemInArr[prop]); } }
edqwerty
7

Это старый вопрос, но недавно я столкнулся с этой проблемой при публикации объектов вместе с файлами. Мне нужно было публиковать объект с дочерними свойствами, которые также были объектами и массивами.

Функция ниже будет проходить через объект и создавать правильный объект 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);
      }
    });
  }
}

Это преобразует следующий json -

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

в следующие FormData

 name, starwars
 year, 1977
 characters[good][], luke
 characters[good][], leia
 characters[bad][], vader
VtoCorleone
источник
Это было полезно для меня, просто нужно было применить String (value) к значению внутри append (в противном случае он не работает для true / false). Также это должно быть, (value !== null) && formData.append(key, value)а не просто formData.append(key, value)иначе, он не работает при нулевых значениях
Александр
7

Версия машинописного текста:

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

С помощью:

let formData = Utility.convertModelToFormData(model);
Мохаммад Дайян
источник
отличная работа, супер полезная: D
Козимо Челлини
3

добавить все входные данные типа в 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
источник
2

Если у вас есть вложенные объекты и массивы, лучший способ заполнить объект FormData - использовать рекурсию.

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
источник
1

Следующая версия действительна для модели, содержащей массивы простых значений:

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

На основе ответа @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);
    }
}

Пример использования:

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

Отправленные данные:

data[a]=1&
data[b]=2&
data[c][d]=3
дикирил
источник