กำลังส่ง multipart / formdata ด้วย jQuery.ajax


563

ฉันมีปัญหาในการส่งไฟล์ไปยังสคริปต์ PHP ฝั่งเซิร์ฟเวอร์โดยใช้ ajax-function ของ jQuery เป็นไปได้ที่จะได้รับ File-List $('#fileinput').attr('files')แต่จะเป็นไปได้อย่างไรในการส่งข้อมูลนี้ไปยังเซิร์ฟเวอร์ อาร์เรย์ผลลัพธ์ ( $_POST) บนเซิร์ฟเวอร์ php-script คือ 0 ( NULL) เมื่อใช้ไฟล์ - อินพุต

ฉันรู้ว่ามันเป็นไปได้ (แม้ว่าฉันจะไม่พบโซลูชัน jQuery ใด ๆ จนกระทั่งตอนนี้มีเพียงรหัส Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multiple-upload-with-progress.html ) )

ดูเหมือนว่าจะค่อนข้างใหม่ดังนั้นโปรดอย่าพูดถึงการอัปโหลดไฟล์จะเป็นไปไม่ได้ผ่าน XHR / Ajax เพราะมันใช้งานได้อย่างแน่นอน

ฉันต้องการฟังก์ชั่นใน Safari 5, FF และ Chrome จะดี แต่ไม่จำเป็น

รหัสของฉันตอนนี้คือ:

$.ajax({
    url: 'php/upload.php',
    data: $('#file').attr('files'),
    cache: false,
    contentType: 'multipart/form-data',
    processData: false,
    type: 'POST',
    success: function(data){
        alert(data);
    }
});

น่าเศร้าที่ใช้วัตถุ FormData ไม่ทำงานบน IE <10
Alejandro García Iglesias

@GarciaWebDev สมมุติว่าคุณสามารถใช้ polyfill พร้อมแฟลชเพื่อรองรับ API เดียวกัน ดูgithub.com/Modernizr/Modernizr/wiki/…สำหรับข้อมูลเพิ่มเติม
yuxhuang

เป็นไปได้ที่ซ้ำกัน
Raphael Schweikert

3
คุณสามารถใช้$(':file')เพื่อเลือกไฟล์อินพุตทั้งหมด มันง่ายกว่านิดหน่อย
Shahar

@RameshwarVyevhare คำตอบนั้นโพสต์ห้าปีหลังจากตอบคำถามนี้ โปรดอย่าหมุนรอบคำถามที่คล้ายกันเพียงเพื่อส่งเสริมคำตอบของคุณเอง
Ryan P

คำตอบ:


882

เริ่มต้นด้วย Safari 5 / Firefox 4 เป็นวิธีที่ง่ายที่สุดในการใช้FormDataคลาส:

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

ดังนั้นตอนนี้คุณมีFormDataวัตถุพร้อมที่จะส่งพร้อมกับ 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);
    }
});

จำเป็นอย่างยิ่งที่คุณต้องตั้งค่าcontentTypeตัวเลือกให้falseบังคับให้ jQuery ไม่ต้องเพิ่มContent-Typeส่วนหัวให้กับคุณมิฉะนั้นสตริงขอบเขตจะหายไป นอกจากนี้คุณต้องปล่อยให้processDataค่าสถานะเป็นเท็จมิฉะนั้น jQuery จะพยายามแปลงสถานะของคุณFormDataเป็นสตริงซึ่งจะล้มเหลว

ตอนนี้คุณสามารถเรียกคืนไฟล์ใน PHP โดยใช้:

$_FILES['file-0']

(มีเพียงไฟล์เดียวเท่านั้นfile-0ยกเว้นว่าคุณได้ระบุแอmultipleททริบิวในไฟล์ของคุณซึ่งในกรณีนี้ตัวเลขจะเพิ่มขึ้นในแต่ละไฟล์)

การใช้การจำลอง FormDataสำหรับเบราว์เซอร์รุ่นเก่า

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

สร้าง FormData จากแบบฟอร์มที่มีอยู่

แทนที่จะทำการวนซ้ำไฟล์ด้วยตนเองวัตถุ FormData ยังสามารถสร้างขึ้นด้วยเนื้อหาของวัตถุฟอร์มที่มีอยู่:

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

ใช้อาร์เรย์เนทีฟ PHP แทนตัวนับ

เพียงตั้งชื่อองค์ประกอบไฟล์ของคุณให้เหมือนกันและจบชื่อด้วยเครื่องหมายวงเล็บ:

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

$_FILES['file']จะเป็นอาร์เรย์ที่มีฟิลด์การอัปโหลดไฟล์สำหรับทุกไฟล์ที่อัปโหลด ฉันแนะนำสิ่งนี้ให้มากกว่าโซลูชันเริ่มต้นของฉันเพราะมันง่ายกว่าที่จะทำซ้ำ


2
นอกจากนี้ยังมีการจำลอง FormDataซึ่งจะทำให้การย้ายโซลูชันนี้ไปยังเบราว์เซอร์รุ่นเก่านั้นค่อนข้างง่าย สิ่งที่คุณต้องทำคือการตรวจสอบdata.fakeและการตั้งค่าcontentTypeสถานที่ให้บริการด้วยตนเองเช่นเดียวกับที่เอาชนะ XHR sendAsBinary()กับการใช้งาน
Raphael Schweikert

13
ดังนั้นคุณไม่ได้ใช้jQueryหรือFormDataชั้นเรียนและคุณถามฉันในบริบทของคำถามเฉพาะ jQuery และคำตอบเฉพาะสำหรับการใช้ FormData? ฉันขอโทษ แต่ฉันไม่คิดว่าฉันสามารถช่วยคุณได้ที่นั่น ...
Raphael Schweikert

3
application/x-www-form-urlencodedใช้นี้ มีวิธีใช้multipart/form-dataแทนหรือไม่
Timmmm

4
@Timmmm multipart/form-dataไม่มีจะใช้ การใช้application/x-www-form-urlencodedจะไม่ทำงาน
Raphael Schweikert

5
@RoyiNamir มันเป็นเพียงเอกสารในรหัสฉันกลัว
Raphael Schweikert

51

แค่อยากจะเพิ่มเล็กน้อยในคำตอบที่ยอดเยี่ยมของราฟาเอล ต่อไปนี้เป็นวิธีทำให้ PHP สร้างสิ่งเดียวกัน$_FILESโดยไม่คำนึงว่าคุณใช้ JavaScript เพื่อส่ง

แบบฟอร์ม 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 สร้างสิ่งนี้$_FILESเมื่อส่งโดยไม่มี 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
                )

        )

)

หากคุณเพิ่มประสิทธิภาพแบบก้าวหน้าโดยใช้ JS ของ Raphael เพื่อส่งไฟล์ ...

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

... นี่คือ$_FILESลักษณะของ PHP หลังจากใช้ JavaScript เพื่อส่ง:

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
        )

)

นั่นเป็นอาร์เรย์ที่ดีและที่จริงแล้วบางคนเปลี่ยน$_FILESไป แต่ฉันคิดว่ามันมีประโยชน์ในการทำงานกับสิ่งเดียวกัน$_FILESโดยไม่คำนึงว่าจะใช้ JavaScript เพื่อส่งหรือไม่ ดังนั้นนี่คือการเปลี่ยนแปลงเล็กน้อยของ 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 เมษายน 2560 แก้ไข: ฉันลบองค์ประกอบของแบบฟอร์มออกจาก Constructor ของ FormData () - ที่แก้ไขรหัสนี้ใน Safari)

รหัสนั้นทำสองสิ่ง

  1. ดึงข้อมูลinputชื่อโดยอัตโนมัติทำให้ HTML สามารถบำรุงรักษาได้มากขึ้น ตอนนี้ตราบใดformที่ชั้นเรียนมีการใส่ทุกอย่างอื่นก็จะได้รับการดูแลโดยอัตโนมัติ นั่นคือinputไม่จำเป็นต้องมีชื่อพิเศษใด ๆ
  2. รูปแบบอาร์เรย์ที่ HTML ส่งปกติถูกสร้างขึ้นใหม่โดย JavaScript ในบรรทัด data.append สังเกตเครื่องหมายวงเล็บ

ด้วยการเปลี่ยนแปลงเหล่านี้ตอนนี้การส่งด้วย JavaScript จะสร้าง$_FILESอาเรย์เดียวกับการส่งด้วย HTML อย่างง่าย


มีปัญหาเดียวกันกับ Safari ขอบคุณสำหรับคำใบ้!
สื่อกลางวันที่

49

ดูรหัสของฉันมันใช้งานได้สำหรับฉัน

$( '#formId' )
  .submit( function( e ) {
    $.ajax( {
      url: 'FormSubmitUrl',
      type: 'POST',
      data: new FormData( this ),
      processData: false,
      contentType: false
    } );
    e.preventDefault();
  } );

มันทำงานได้อย่างสมบูรณ์แบบและใช้งานง่ายมาก
Jh62

45

ฉันเพิ่งสร้างฟังก์ชั่นนี้ตามข้อมูลที่ฉันอ่าน

ใช้เช่นการใช้แทนเพียงแค่ใส่.serialize() ทำงานที่นี่ในการทดสอบของฉัน.serializefiles();

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

2
ฉันพยายามทำให้มันใช้งานได้ แต่ดูเหมือนจะไม่รู้จัก serializefiles () เป็นฟังก์ชันแม้ว่าคำจำกัดความนี้จะอยู่ที่ด้านบนของหน้า
Fallenreaper

1
ที่เหมาะกับฉันได้ดี ได้รับข้อมูลที่มี var data = $("#avatar-form").serializefiles();การส่งผ่านข้อมูลพารามิเตอร์อาแจ็กซ์และการวิเคราะห์ด้วยด่วนที่น่ากลัว: form.parse(req, function(err, fields, files){ขอขอบคุณสำหรับโค้ดที่ :)
SchurigH

23

หากแบบฟอร์มของคุณถูกกำหนดใน HTML ของคุณมันจะง่ายกว่าที่จะส่งแบบฟอร์มไปยังตัวสร้างมากกว่าที่จะทำซ้ำและเพิ่มภาพ

$('#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){
            ...

9

คำตอบของ Devin Venableนั้นใกล้กับสิ่งที่ฉันต้องการ แต่ฉันต้องการหนึ่งที่สามารถทำงานได้หลายรูปแบบและใช้การกระทำที่ระบุไว้แล้วในแบบฟอร์มเพื่อให้แต่ละไฟล์จะไปถูกที่แล้ว

ฉันต้องการใช้วิธี on () ของ jQuery เพื่อหลีกเลี่ยงการใช้. ready ()

นั่นทำให้ฉันเป็นแบบนี้: (แทนที่ formSelector ด้วย jQuery selector ของคุณ)

$(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.
    });

});

2

ทุกวันนี้คุณไม่จำเป็นต้องมี jQuery :) เรียกตารางการสนับสนุน API

let result = fetch('url', {method: 'POST', body: new FormData(document.querySelector("#form"))})

ในบริบทของการอัปโหลดไฟล์ผ่านfetchโปรดดูความเข้ากันได้: developer.mozilla.org/en-US/docs/Web/API/
......

@ OmarTariq ใช่ฉันมีลิงก์ที่คล้ายกันในคำตอบของฉัน))
Alex Nikulin

อุ่ย ฉันจะพลาดได้อย่างไร:-|
Omar Tariq

1

คลาส FormData ทำงานได้ แต่ใน iOS Safari (บน iPhone อย่างน้อย) ฉันไม่สามารถใช้โซลูชันของ Raphael Schweikert อย่างที่เป็นอยู่

Mozilla Dev มีหน้าที่ดีในการจัดการกับวัตถุ FormData

ดังนั้นเพิ่มรูปแบบที่ว่างบางแห่งในหน้าของคุณโดยระบุประเภท:

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

จากนั้นสร้างวัตถุ FormData เป็น:

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

และดำเนินการในรหัสของราฟาเอล


ฉันมีปัญหากับ jquery ajax ของฉันอัพโหลดอยู่ใน Safari อย่างเงียบ ๆ และลงเอยด้วยการทำเบราว์เซอร์ที่มีเงื่อนไข $ ('form-name') ส่ง () สำหรับ Safari แทนการอัปโหลด ajax ที่ทำงานใน IE9 และ FF18 อาจไม่ใช่โซลูชันที่สมบูรณ์แบบสำหรับการอัปโหลดหลายรายการ แต่ฉันกำลังทำสิ่งนี้เพื่อไฟล์เดียวเป็น iframe จากกล่องโต้ตอบ jquery เพื่อให้สามารถใช้งานได้
glyph

1

หนึ่ง gotcha ที่ฉันพบเจอในวันนี้ฉันคิดว่ามันคุ้มค่าที่จะชี้ให้เห็นว่าเกี่ยวข้องกับปัญหานี้: หาก url สำหรับการโทร ajax ถูกเปลี่ยนเส้นทางส่วนหัวของประเภทเนื้อหา: 'multipart / form-data' อาจสูญหายได้

ตัวอย่างเช่นฉันโพสต์ไปที่http://server.com/context?param=x

ในแท็บเครือข่ายของ Chrome ฉันเห็นส่วนหัวหลายส่วนที่ถูกต้องสำหรับคำขอนี้ แต่จากนั้นเปลี่ยนเส้นทาง 302 เป็นhttp://server.com/context/?param=x (สังเกตเครื่องหมายทับหลังบริบท)

ในระหว่างการเปลี่ยนเส้นทางส่วนหัวของ multipart ก็หายไป ตรวจสอบให้แน่ใจว่าคำขอไม่ได้ถูกเปลี่ยนเส้นทางหากโซลูชันเหล่านี้ไม่ทำงานสำหรับคุณ


1

โซลูชันทั้งหมดข้างต้นนั้นดูดีและสวยงาม แต่วัตถุ FormData () ไม่ได้คาดหวังว่าพารามิเตอร์ใด ๆ แต่ใช้ผนวก () หลังจากอินสแตนซ์ของมันเช่นเดียวกับที่เขียนไว้ด้านบน:

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


1

หากอินพุตไฟล์nameระบุอาร์เรย์และแฟล็multipleกและคุณแยกวิเคราะห์ทั้งหมดformด้วยFormDataไม่จำเป็นต้องวนซ้ำappend()ไฟล์อินพุต FormDataจะจัดการไฟล์หลายไฟล์โดยอัตโนมัติ

$('#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>

โปรดทราบว่าปกติจะใช้ไม่ได้button type="button" type="submit"สิ่งนี้แสดงให้เห็นว่าไม่มีการพึ่งพาการใช้submitเพื่อรับฟังก์ชั่นนี้

$_FILESรายการผลลัพธ์เป็นเช่นนี้ในเครื่องมือพัฒนา 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"]

หมายเหตุ: มีหลายกรณีที่บางภาพจะอัปโหลดได้ดีเมื่ออัปโหลดเป็นไฟล์เดียว แต่จะล้มเหลวเมื่ออัปโหลดในชุดของไฟล์หลายไฟล์ อาการคือ PHP รายงานว่าว่างเปล่า$_POSTและ$_FILESไม่มี AJAX ทิ้งข้อผิดพลาดใด ๆ ปัญหาเกิดขึ้นกับ Chrome 75.0.3770.100 และ PHP 7.0 ดูเหมือนจะเกิดขึ้นกับ 1 ในภาพหลายโหลในชุดทดสอบของฉัน


0

IE เวอร์ชันเก่ากว่าไม่รองรับ FormData (รายการสนับสนุนเบราว์เซอร์แบบเต็มสำหรับ FormData อยู่ที่นี่: https://developer.mozilla.org/en-US/docs/Web/API/FormData )

คุณสามารถใช้ปลั๊กอิน jquery (ตัวอย่างเช่นhttp://malsup.com/jquery/form/#code-samples ) หรือคุณสามารถใช้ IFrame based solution เพื่อโพสต์ข้อมูลฟอร์มหลายส่วนผ่าน ajax: https: // developer mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_through_JavaScript


0

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery และรูปแบบ 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>


-1
  1. รับวัตถุรูปแบบโดย jquery-> $ ("# id") [0]
  2. data = new FormData ($ ("# id") [0]);
  3. ตกลงข้อมูลเป็นสิ่งที่คุณต้องการ

$ ("# id") [0] กลับมาเป็นอันดับแรกไม่มีว่าง <input type = "file" /> ของแบบฟอร์มคุณจะส่งแบบฟอร์มทั้งหมดได้อย่างไรรวมทั้ง <input type = "file" /> ทั้งหมด
Mohammad-Hossein Jamali
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.