Gửi nhiều trang / formdata với jQuery.ajax


563

Tôi đã gặp sự cố khi gửi tệp tới tập lệnh PHP bên máy chủ bằng cách sử dụng chức năng ajax của jQuery. Có thể lấy Danh sách tệp với $('#fileinput').attr('files')nhưng làm cách nào để gửi Dữ liệu này đến máy chủ? Mảng kết quả ( $_POST) trên tập lệnh php-server là 0 ( NULL) khi sử dụng tập tin đầu vào.

Tôi biết điều đó là có thể (mặc dù tôi không tìm thấy bất kỳ giải pháp jQuery nào cho đến bây giờ, chỉ có mã Prototye ( http://webreflection.blogspot.com/2009/03/safari-4-multipl-upload-with-proTHER.html ) ).

Điều này dường như là tương đối mới, vì vậy xin vui lòng không đề cập đến việc tải lên tệp sẽ là không thể thông qua XHR / Ajax, vì nó chắc chắn hoạt động.

Tôi cần chức năng trong Safari 5, FF và Chrome sẽ tốt nhưng không cần thiết.

Mã của tôi bây giờ là:

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

Đáng buồn là sử dụng đối tượng FormData không hoạt động trên IE <10.
Alejandro García Iglesias

@GarciaWebDev được cho là bạn có thể sử dụng một polyfill với Flash để hỗ trợ cùng API. Kiểm tra github.com/Modernizr/Modernizr/wiki/ cấp để biết thêm thông tin.
yuxhuang


3
Bạn có thể sử dụng $(':file')để chọn tất cả các tập tin đầu vào. Nó chỉ đơn giản hơn một chút.
Shahar

@RameshwarVyevhare Câu trả lời đó đã được đăng năm năm sau khi câu hỏi này được trả lời. Xin đừng troll các câu hỏi tương tự chỉ để thúc đẩy câu trả lời của riêng bạn.
Ryan P

Câu trả lời:


882

Bắt đầu với Safari 5 / Firefox 4, lớp này dễ sử dụng FormDatanhất:

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

Vì vậy, bây giờ bạn có một FormDatađối tượng, sẵn sàng để được gửi cùng với 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);
    }
});

Điều bắt buộc là bạn phải đặt contentTypetùy chọn thành false, buộc jQuery không thêm Content-Typetiêu đề cho bạn, nếu không, chuỗi ranh giới sẽ bị thiếu khỏi nó. Ngoài ra, bạn phải để processDatacờ được đặt thành false, nếu không, jQuery sẽ cố gắng chuyển đổi FormDatachuỗi của bạn thành một chuỗi, điều này sẽ thất bại.

Bây giờ bạn có thể truy xuất tệp trong PHP bằng cách sử dụng:

$_FILES['file-0']

(Chỉ có một tệp, file-0trừ khi bạn chỉ định multiplethuộc tính trên đầu vào tệp của mình, trong trường hợp đó, các số sẽ tăng theo từng tệp.)

Sử dụng mô phỏng FormData cho các trình duyệt cũ hơn

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

Tạo FormData từ một biểu mẫu hiện có

Thay vì lặp lại thủ công các tệp, đối tượng FormData cũng có thể được tạo bằng nội dung của đối tượng biểu mẫu hiện có:

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

Sử dụng một mảng nguyên gốc PHP thay vì bộ đếm

Chỉ cần đặt tên các thành phần tệp của bạn giống nhau và kết thúc tên trong ngoặc:

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

$_FILES['file']sau đó sẽ là một mảng chứa các trường tải lên tệp cho mỗi tệp được tải lên. Tôi thực sự khuyên bạn nên giải pháp này qua giải pháp ban đầu của mình vì việc lặp lại đơn giản hơn.


2
Ngoài ra, có một mô phỏng FormData sẽ giúp chuyển giải pháp này sang các trình duyệt cũ hơn khá đơn giản. Tất cả bạn phải làm là kiểm tra data.fakevà thiết lập thuộc contentTypetính theo cách thủ công cũng như ghi đè xhr để sử dụng sendAsBinary().
Raphael Schweikert

13
Vì vậy, bạn không sử dụng jQuerycũng không phải FormDatalớp và bạn đang hỏi tôi trong bối cảnh câu hỏi dành riêng cho jQuery và câu trả lời cụ thể cho việc sử dụng FormData? Tôi xin lỗi nhưng tôi không nghĩ mình có thể giúp bạn ở đó
Raphael Schweikert

3
Điều này sử dụng application/x-www-form-urlencoded. Có cách nào để sử dụng multipart/form-datathay thế?
Timmmm

4
@Timmmm Không, nó sử dụng multipart/form-data. Sử dụng application/x-www-form-urlencodedsẽ không hoạt động.
Raphael Schweikert

5
@RoyiNamir Nó chỉ được ghi lại bằng mã , tôi sợ.
Raphael Schweikert

51

Chỉ muốn thêm một chút vào câu trả lời tuyệt vời của Raphael. Đây là cách để PHP sản xuất giống nhau $_FILES, bất kể bạn có sử dụng JavaScript để gửi hay không.

Mẫu 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 tạo ra điều này $_FILES, khi được gửi mà không có 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
                )

        )

)

Nếu bạn thực hiện nâng cao lũy tiến, sử dụng JS của Raphael để gửi các tệp ...

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

... đây là $_FILESmảng của PHP trông như thế nào, sau khi sử dụng JavaScript đó để gửi:

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
        )

)

Đó là một mảng đẹp và thực sự là thứ mà một số người biến $_FILESthành, nhưng tôi thấy thật hữu ích khi làm việc với cùng $_FILES, bất kể JavaScript có được sử dụng để gửi hay không. Vì vậy, đây là một số thay đổi nhỏ đối với 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);
});

(Chỉnh sửa ngày 14 tháng 4 năm 2017: Tôi đã xóa phần tử biểu mẫu khỏi hàm tạo của FormData () - đã sửa mã này trong Safari.)

Mã đó làm hai điều.

  1. Lấy inputthuộc tính tên tự động, làm cho HTML dễ bảo trì hơn. Bây giờ, miễn là formlớp putImages, mọi thứ khác sẽ được xử lý tự động. Đó là, inputkhông cần có bất kỳ tên đặc biệt.
  2. Định dạng mảng mà HTML thông thường gửi được JavaScript tạo lại trong dòng data.append. Lưu ý các dấu ngoặc.

Với những thay đổi này, việc gửi bằng JavaScript hiện tạo ra chính xác cùng một $_FILESmảng với việc gửi bằng HTML đơn giản.


Có vấn đề tương tự với Safari. Cảm ơn các gợi ý!
hòa giải

49

Nhìn vào mã của tôi, nó làm việc cho tôi

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

Điều này làm việc hoàn hảo và rất đơn giản để thực hiện.
Jh62

45

Tôi chỉ xây dựng chức năng này dựa trên một số thông tin tôi đọc.

Sử dụng nó như sử dụng .serialize(), thay vì chỉ cần đặt .serializefiles();.
Làm việc ở đây trong các bài kiểm tra của tôi.

//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
Tôi đã cố gắng để làm việc này, nhưng dường như không nhận ra serializefiles () là một hàm, mặc dù định nghĩa này nằm ở đầu trang.
Fallenreaper

1
điều đó làm việc cho tôi tốt nhận dữ liệu bằng var data = $("#avatar-form").serializefiles();cách gửi thông tin này qua tham số dữ liệu ajax và phân tích với tốc độ ghê gớm: form.parse(req, function(err, fields, files){cảm ơn bạn vì đoạn mã đó :)
SchurigH

23

Nếu biểu mẫu của bạn được xác định trong HTML của bạn, việc chuyển biểu mẫu vào hàm tạo sẽ dễ dàng hơn so với việc lặp lại và thêm hình ảnh.

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

Câu trả lời của Devin Venable gần với những gì tôi muốn, nhưng tôi muốn một câu trả lời sẽ hoạt động trên nhiều biểu mẫu và sử dụng hành động đã được chỉ định trong biểu mẫu để mỗi tệp sẽ đến đúng nơi.

Tôi cũng muốn sử dụng phương thức on () của jQuery để tôi có thể tránh sử dụng. Yet ().

Điều đó đã đưa tôi đến điều này: (thay thế formSelector bằng bộ chọn jQuery của bạn)

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

Ngày nay, bạn thậm chí không cần jQuery :) tìm nạp bảng hỗ trợ API

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

Trong ngữ cảnh tải tệp qua fetchxin vui lòng xem khả năng tương thích: developer.mozilla.org/en-US/docs/Web/API/
mẹo

@OmarTariq vâng Tôi có một liên kết tương tự trong câu trả lời của tôi))
Alex Nikulin

Giáo sư. Làm thế nào tôi có thể bỏ lỡ điều đó:-|
Omar Tariq

1

Lớp FormData hoạt động, tuy nhiên trong iOS Safari (ít nhất là trên iPhone) tôi đã không thể sử dụng giải pháp của Raphael Schweikert.

Mozilla Dev có một trang đẹp về thao tác với các đối tượng FormData .

Vì vậy, hãy thêm một biểu mẫu trống ở đâu đó trong trang của bạn, chỉ định mã hóa:

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

Sau đó, tạo đối tượng FormData là:

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

và tiến hành như trong mã của Raphael .


Tôi gặp vấn đề với việc tải lên ajax jquery của mình âm thầm treo trong Safari và cuối cùng làm một trình duyệt có điều kiện $ ('tên mẫu'). Gửi () cho Safari thay vì tải lên ajax hoạt động trong IE9 và FF18. Có lẽ không phải là một giải pháp lý tưởng cho nhiều lượt tải lên nhưng tôi đã thực hiện điều này cho một tệp duy nhất vào iframe từ hộp thoại jquery để nó hoạt động tốt.
glyph

1

Một vấn đề tôi gặp phải hôm nay tôi nghĩ là đáng để chỉ ra liên quan đến vấn đề này: nếu url cho cuộc gọi ajax được chuyển hướng thì tiêu đề cho kiểu nội dung: 'nhiều dữ liệu / biểu mẫu dữ liệu' có thể bị mất.

Ví dụ: tôi đã đăng lên http://server.com/context?param=x

Trong tab mạng của Chrome, tôi đã thấy tiêu đề nhiều phần chính xác cho yêu cầu này nhưng sau đó chuyển hướng 302 sang http://server.com/context/?param=x (lưu ý dấu gạch chéo sau ngữ cảnh)

Trong quá trình chuyển hướng, tiêu đề nhiều phần bị mất. Đảm bảo các yêu cầu không được chuyển hướng nếu các giải pháp này không hiệu quả với bạn.


1

Tất cả các giải pháp ở trên đều có vẻ tốt và thanh lịch, nhưng đối tượng FormData () không mong đợi bất kỳ tham số nào, nhưng sử dụng append () sau khi khởi tạo nó, giống như những gì đã viết ở trên:

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


1

Nếu đầu vào tệp namechỉ ra một mảng và cờ multiplevà bạn phân tích toàn bộ formvới FormData, thì không cần thiết phải lặp lại append()các tệp đầu vào. FormDatasẽ tự động xử lý nhiều tập tin.

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

Lưu ý rằng một thường xuyên button type="button"được sử dụng, không type="submit". Điều này cho thấy không có sự phụ thuộc vào việc sử dụng submitđể có được chức năng này.

Mục kết quả $_FILESgiống như thế này trong các công cụ phát triển của 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"]

Lưu ý: Có những trường hợp một số hình ảnh sẽ tải lên tốt khi được tải lên dưới dạng một tệp, nhưng chúng sẽ thất bại khi được tải lên trong một tập hợp nhiều tệp. Triệu chứng là PHP báo cáo trống $_POST$_FILESkhông có AJAX ném bất kỳ lỗi nào. Sự cố xảy ra với Chrome 75.0.3770.100 và PHP 7.0. Chỉ có vẻ như xảy ra với 1 trong số vài chục hình ảnh trong bộ thử nghiệm của tôi.


0

Các phiên bản IE cũ hơn không hỗ trợ FormData (Danh sách hỗ trợ trình duyệt đầy đủ cho FormData có tại đây: https://developer.mozilla.org/en-US/docs/Web/API/FormData ).

Bạn có thể sử dụng plugin jquery (Dành cho ex, http://malsup.com/jquery/form/#code-samples ) hoặc, bạn có thể sử dụng giải pháp dựa trên IFrame để đăng dữ liệu biểu mẫu nhiều phần thông qua ajax: https: // developer. mozilla.org/en-US/docs/Learn/HTML/Forms/Sending_forms_ENC_JavaScript


0

PHP

<?php 
    var_dump( $_FILES ); 
?>

jQuery và 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>


-1
  1. lấy đối tượng biểu mẫu bằng jquery-> $ ("# id") [0]
  2. dữ liệu = FormData mới ($ ("# id") [0]);
  3. ok, dữ liệu là mong muốn của bạn

$ ("# id") [0] trả về đầu tiên không có trống <input type = "file" /> của biểu mẫu, làm thế nào để bạn gửi toàn bộ biểu mẫu bao gồm tất cả <input type = "file" /> của nó?
Mohammad-Hossein Jamali
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.