Mengirim multipart / formdata dengan jQuery.ajax

563

Saya punya masalah dalam mengirim file ke skrip PHP di sisi server menggunakan fungsi ajax jQuery. Mungkin untuk mendapatkan File-List dengan $('#fileinput').attr('files')tetapi bagaimana mungkin untuk mengirim data ini ke server? Array yang dihasilkan ( $_POST) pada script-serveride php adalah 0 ( NULL) saat menggunakan input file.

Saya tahu itu mungkin (meskipun saya tidak menemukan solusi jQuery sampai sekarang, hanya kode Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html ) ).

Ini tampaknya relatif baru, jadi tolong jangan menyebutkan bahwa pengunggahan file tidak mungkin dilakukan melalui XHR / Ajax, karena itu pasti berfungsi.

Saya membutuhkan fungsionalitas di Safari 5, FF dan Chrome akan menyenangkan tetapi tidak penting.

Kode saya untuk saat ini adalah:

$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});
zoku
sumber
Sayangnya menggunakan objek FormData tidak berfungsi di IE <10.
Alejandro García Iglesias
@GarciaWebDev seharusnya Anda dapat menggunakan polyfill dengan Flash untuk mendukung API yang sama. Lihat github.com/Modernizr/Modernizr/wiki/… untuk info lebih lanjut.
yuxhuang
Kemungkinan duplikat .
Raphael Schweikert
3
Anda dapat menggunakan $(':file')untuk memilih semua file input. Itu hanya sedikit lebih sederhana.
Shahar
@RameshwarVyevhare Jawaban itu diposting lima tahun setelah pertanyaan ini dijawab. Tolong jangan troll pertanyaan serupa hanya untuk mempromosikan jawaban Anda sendiri.
Ryan P

Jawaban:

882

Dimulai dengan Safari 5 / Firefox 4, paling mudah menggunakan FormDatakelas:

var data = new FormData();
jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file-'+i, file);
});

Jadi sekarang Anda memiliki FormDataobjek, siap dikirim bersama dengan XMLHttpRequest.

jQuery.ajax({
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
});

Sangat penting bagi Anda untuk mengatur contentTypeopsi false, memaksa jQuery untuk tidak menambahkan Content-Typeheader untuk Anda, jika tidak, string batas akan hilang darinya. Selain itu, Anda harus membiarkan processDataflag disetel ke false, jika tidak, jQuery akan mencoba mengubah Anda FormDatamenjadi string, yang akan gagal.

Anda sekarang dapat mengambil file dalam PHP menggunakan:

$_FILES['file-0']

(Hanya ada satu file,, file-0kecuali jika Anda menentukan multipleatribut pada input file Anda, dalam hal ini, angkanya akan bertambah dengan setiap file.)

Menggunakan emulasi FormData untuk browser yang lebih lama

var opts = {
    url: 'php/upload.php',
    data: data,
    cache: false,
    contentType: false,
    processData: false,
    method: 'POST',
    type: 'POST', // For jQuery < 1.9
    success: function(data){
        alert(data);
    }
};
if(data.fake) {
    // Make sure no text encoding stuff is done by xhr
    opts.xhr = function() { var xhr = jQuery.ajaxSettings.xhr(); xhr.send = xhr.sendAsBinary; return xhr; }
    opts.contentType = "multipart/form-data; boundary="+data.boundary;
    opts.data = data.toString();
}
jQuery.ajax(opts);

Buat FormData dari formulir yang ada

Alih-alih secara manual mengulangi file, objek FormData juga dapat dibuat dengan konten objek formulir yang ada:

var data = new FormData(jQuery('form')[0]);

Gunakan array asli PHP alih-alih penghitung

Cukup beri nama elemen file Anda sama dan akhiri nama dalam tanda kurung:

jQuery.each(jQuery('#file')[0].files, function(i, file) {
    data.append('file[]', file);
});

$_FILES['file']maka akan menjadi array yang berisi bidang unggahan file untuk setiap file yang diunggah. Saya sebenarnya merekomendasikan ini di atas solusi awal saya karena lebih mudah untuk beralih.

Raphael Schweikert
sumber
2
Juga, ada emulasi FormData yang akan membuat porting solusi ini ke browser lama cukup sederhana. Yang harus Anda lakukan adalah memeriksa data.fakedan mengatur contentTypeproperti secara manual serta mengganti xhr untuk digunakan sendAsBinary().
Raphael Schweikert
13
Jadi Anda tidak menggunakan jQueryatau FormDatakelas dan Anda bertanya kepada saya dalam konteks pertanyaan khusus untuk jQuery dan jawaban khusus untuk menggunakan FormData? Maaf, tapi saya rasa saya tidak bisa membantu Anda di sana ...
Raphael Schweikert
3
Ini menggunakan application/x-www-form-urlencoded. Apakah ada cara untuk menggunakannya multipart/form-data?
Timmmm
4
@ Timmmm Tidak, ini digunakan multipart/form-data. Menggunakan application/x-www-form-urlencodedtidak akan berhasil.
Raphael Schweikert
5
@RoyiNamir Ini hanya didokumentasikan dalam kode , saya rasa.
Raphael Schweikert
51

Hanya ingin menambahkan sedikit jawaban Raphael yang luar biasa. Berikut cara mendapatkan PHP untuk menghasilkan yang sama $_FILES, terlepas dari apakah Anda menggunakan JavaScript untuk mengirim.

Bentuk HTML:

<form enctype="multipart/form-data" action="/test.php" 
method="post" class="putImages">
   <input name="media[]" type="file" multiple/>
   <input class="button" type="submit" alt="Upload" value="Upload" />
</form>

PHP menghasilkan ini $_FILES, ketika dikirimkan tanpa JavaScript:

Array
(
    [media] => Array
        (
            [name] => Array
                (
                    [0] => Galata_Tower.jpg
                    [1] => 518f.jpg
                )

            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )

            [tmp_name] => Array
                (
                    [0] => /tmp/phpIQaOYo
                    [1] => /tmp/phpJQaOYo
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )

            [size] => Array
                (
                    [0] => 258004
                    [1] => 127884
                )

        )

)

Jika Anda melakukan peningkatan progresif, gunakan Raphael's JS untuk mengirim file ...

var data = new FormData($('input[name^="media"]'));     
jQuery.each($('input[name^="media"]')[0].files, function(i, file) {
    data.append(i, file);
});

$.ajax({
    type: ppiFormMethod,
    data: data,
    url: ppiFormActionURL,
    cache: false,
    contentType: false,
    processData: false,
    success: function(data){
        alert(data);
    }
});

... beginilah bentuk $_FILESarray PHP , setelah menggunakan JavaScript itu untuk mengirim:

Array
(
    [0] => Array
        (
            [name] => Galata_Tower.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpAQaOYo
            [error] => 0
            [size] => 258004
        )

    [1] => Array
        (
            [name] => 518f.jpg
            [type] => image/jpeg
            [tmp_name] => /tmp/phpBQaOYo
            [error] => 0
            [size] => 127884
        )

)

Itu array yang bagus, dan sebenarnya apa yang ditransformasikan oleh beberapa orang $_FILES, tetapi saya merasa bermanfaat untuk bekerja dengan yang sama $_FILES, terlepas dari apakah JavaScript digunakan untuk mengirim. Jadi, berikut adalah beberapa perubahan kecil pada JS:

// match anything not a [ or ]
regexp = /^[^[\]]+/;
var fileInput = $('.putImages input[type="file"]');
var fileInputName = regexp.exec( fileInput.attr('name') );

// make files available
var data = new FormData();
jQuery.each($(fileInput)[0].files, function(i, file) {
    data.append(fileInputName+'['+i+']', file);
});

(14 April 2017 sunting: Saya menghapus elemen formulir dari konstruktor FormData () - yang memperbaiki kode ini di Safari.)

Kode itu melakukan dua hal.

  1. Mengambil inputatribut nama secara otomatis, membuat HTML lebih mudah dikelola. Sekarang, selama formmemiliki putImages kelas, semua hal lain diurus secara otomatis. Artinya, inputtidak perlu memiliki nama khusus.
  2. Format array yang dikirimkan HTML normal dibuat ulang oleh JavaScript di baris data.append. Perhatikan tanda kurung.

Dengan perubahan ini, mengirimkan dengan JavaScript sekarang menghasilkan $_FILESarray yang sama persis seperti mengirimkan dengan HTML sederhana.

ajmicek
sumber
Punya masalah yang sama dengan Safari. Terima kasih atas petunjuknya!
medoingthings
49

Lihatlah kode saya, itu berfungsi untuk saya

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );
Asad Malik
sumber
Ini bekerja dengan sempurna dan sangat mudah diterapkan.
Jh62
45

Saya baru saja membangun fungsi ini berdasarkan beberapa info yang saya baca.

Gunakan itu seperti menggunakan .serialize(), bukan hanya menempatkan .serializefiles();.
Bekerja di sini dalam ujian saya.

//USAGE: $("#form").serializefiles();
(function($) {
$.fn.serializefiles = function() {
    var obj = $(this);
    /* ADD FILE TO PARAM AJAX */
    var formData = new FormData();
    $.each($(obj).find("input[type='file']"), function(i, tag) {
        $.each($(tag)[0].files, function(i, file) {
            formData.append(tag.name, file);
        });
    });
    var params = $(obj).serializeArray();
    $.each(params, function (i, val) {
        formData.append(val.name, val.value);
    });
    return formData;
};
})(jQuery);
evandro777
sumber
2
Saya mencoba agar ini berfungsi, tetapi tampaknya tidak mengenali serializefiles () sebagai fungsi, meskipun definisi ini ada di bagian atas halaman.
Fallenreaper
1
itu berfungsi baik untuk saya. mendapatkan data dengan var data = $("#avatar-form").serializefiles();mengirimkan ini melalui parameter data ajax dan menganalisis dengan tangguh ekspres: form.parse(req, function(err, fields, files){terima kasih untuk potongan kode itu :)
SchurigH
23

Jika formulir Anda didefinisikan dalam HTML Anda, lebih mudah untuk memasukkan formulir ke konstruktor daripada untuk iterate dan menambahkan gambar.

$('#my-form').submit( function(e) {
    e.preventDefault();

    var data = new FormData(this); // <-- 'this' is your form element

    $.ajax({
            url: '/my_URL/',
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            type: 'POST',     
            success: function(data){
            ...
Devin Venable
sumber
9

Jawaban Devin Venable dekat dengan apa yang saya inginkan, tetapi saya menginginkan yang akan bekerja pada banyak formulir, dan menggunakan tindakan yang sudah ditentukan dalam formulir sehingga setiap file akan pergi ke tempat yang tepat.

Saya juga ingin menggunakan metode on () jQuery sehingga saya bisa menghindari penggunaan .ready ().

Itu membuat saya seperti ini: (ganti formSelector dengan pemilih jQuery Anda)

$(document).on('submit', formSelecter, function( e ) {
        e.preventDefault();
    $.ajax( {
        url: $(this).attr('action'),
        type: 'POST',
        data: new FormData( this ),
        processData: false,
        contentType: false
    }).done(function( data ) {
        //do stuff with the data you got back.
    });

});
Karl Henselin
sumber
2

Saat ini Anda bahkan tidak perlu jQuery :) mengambil tabel dukungan API

let result = fetch('url', {method: 'POST', body: new FormData(document.querySelector("#form"))})
Alex Nikulin
sumber
Dalam konteks unggahan file melalui fetchsilakan lihat kompatibilitas: developer.mozilla.org/en-US/docs/Web/API/…
Omar Tariq
@OmarTariq ya saya punya tautan serupa dalam jawaban saya))
Alex Nikulin
Ups. Bagaimana saya bisa melewatkan itu:-|
Omar Tariq
1

Kelas FormData berfungsi, namun di iOS Safari (setidaknya di iPhone) saya tidak dapat menggunakan solusi Raphael Schweikert seperti apa adanya.

Mozilla Dev memiliki halaman yang bagus tentang memanipulasi objek FormData .

Jadi, tambahkan formulir kosong di suatu tempat di halaman Anda, tentukan enctype:

<form enctype="multipart/form-data" method="post" name="fileinfo" id="fileinfo"></form>

Lalu, buat objek FormData sebagai:

var data = new FormData($("#fileinfo"));

dan lanjutkan seperti dalam kode Raphael .

topkara
sumber
Saya punya masalah dengan unggah ajax jquery saya yang diam-diam menggantung di Safari dan akhirnya melakukan browser dengan syarat $ ('form-name'). Submit () untuk Safari alih-alih unggahan ajax yang bekerja di IE9 dan FF18. Mungkin bukan solusi yang ideal untuk multi-unggah tetapi saya melakukan ini untuk satu file menjadi iframe dari dialog jquery sehingga berfungsi dengan baik.
mesin terbang
1

Satu gotcha yang saya temui hari ini saya pikir layak untuk ditunjukkan terkait dengan masalah ini: jika url untuk panggilan ajax dialihkan maka header untuk tipe konten: 'multipart / formulir-data' dapat hilang.

Misalnya, saya memposting ke http://server.com/context?param=x

Di tab jaringan Chrome saya melihat header multi-bagian yang benar untuk permintaan ini, tetapi kemudian 302 redirect ke http://server.com/context/?param=x (perhatikan garis miring setelah konteks)

Selama pengalihan header multi bagian hilang. Pastikan permintaan tidak dialihkan jika solusi ini tidak berhasil untuk Anda.

james
sumber
1

Semua solusi di atas terlihat bagus dan elegan, tetapi objek FormData () tidak mengharapkan parameter apa pun, tetapi gunakan append () setelah instantiate, seperti apa yang ditulis di atas:

formData.append (val.name, val.value);

szatti1489
sumber
1

Jika input file namemenunjukkan array dan flag multiple, dan Anda menguraikan keseluruhan formdengan FormData, tidak perlu secara iteratif append()file input. FormDataakan secara otomatis menangani banyak file.

$('#submit_1').on('click', function() {
  let data = new FormData($("#my_form")[0]);

  $.ajax({
    url: '/path/to/php_file',
    type: 'POST',
    data: data,
    processData: false,
    contentType: false,
    success: function(r) {
      console.log('success', r);
    },
    error: function(r) {
      console.log('error', r);
    }
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="my_form">
  <input type="file" name="multi_img_file[]" id="multi_img_file" accept=".gif,.jpg,.jpeg,.png,.svg" multiple="multiple" />
  <button type="button" name="submit_1" id="submit_1">Not type='submit'</button>
</form>

Perhatikan bahwa reguler button type="button"digunakan, bukan type="submit". Ini menunjukkan bahwa tidak ada ketergantungan pada penggunaan submituntuk mendapatkan fungsionalitas ini.

$_FILESEntri yang dihasilkan seperti ini di alat dev Chrome:

multi_img_file:
  error: (2) [0, 0]
  name: (2) ["pic1.jpg", "pic2.jpg"]
  size: (2) [1978036, 2446180]
  tmp_name: (2) ["/tmp/phphnrdPz", "/tmp/phpBrGSZN"]
  type: (2) ["image/jpeg", "image/jpeg"]

Catatan: Ada kasus di mana beberapa gambar akan diunggah dengan baik ketika diunggah sebagai satu file, tetapi mereka akan gagal ketika diunggah dalam satu set beberapa file. Gejalanya adalah bahwa laporan PHP kosong $_POSTdan $_FILEStanpa AJAX membuat kesalahan. Masalah terjadi dengan Chrome 75.0.3770.100 dan PHP 7.0. Tampaknya hanya terjadi dengan 1 dari beberapa lusin gambar dalam set pengujian saya.

OKSIGEN
sumber
0

Versi IE yang lebih lama tidak mendukung FormData (Daftar dukungan browser lengkap untuk FormData ada di sini: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

Entah Anda dapat menggunakan plugin jquery (Sebagai contoh, http://malsup.com/jquery/form/#code-samples ) atau, Anda dapat menggunakan solusi berbasis IFrame untuk mengirim data formulir multi-bagian melalui ajax: https: // developer. mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript

sudip
sumber
0

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery dan Form HTML

$( "#form" ).bind( "submit", function (e) {

    e.preventDefault();

    $.ajax({

        method: "post",
        url: "process.php",

        data: new FormData( $(this) ), // $(this) = formHTML element

        cache: false,

        /* upload */
        contentType: false,
        processData: false

    });

} )
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

<form id="form" >
    File: <input type="file" name="upload" />
    <input type="submit" value="Submit" />
</form>

antelove
sumber
-1
  1. dapatkan objek form dengan jquery-> $ ("# id") [0]
  2. data = FormData baru ($ ("# id") [0]);
  3. ok, data adalah keinginan Anda
pengguna1909226
sumber
$ ("# id") [0] mengembalikan terlebih dahulu tidak ada yang kosong <input type = "file" /> dari formulir, bagaimana Anda mengirimkan seluruh formulir termasuk semua <input type = "file" /> itu?
Mohammad-Hossein Jamali