Tải lên cả dữ liệu và tệp trong một hình thức bằng Ajax?


384

Tôi đang sử dụng jQuery và Ajax cho các biểu mẫu của mình để gửi dữ liệu và tệp nhưng tôi không chắc chắn cách gửi cả dữ liệu và tệp trong một biểu mẫu?

Tôi hiện đang làm gần như giống nhau với cả hai phương thức nhưng cách thức thu thập dữ liệu vào một mảng là khác nhau, dữ liệu sử dụng .serialize();nhưng các tệp sử dụng= new FormData($(this)[0]);

Có thể kết hợp cả hai phương pháp để có thể tải lên các tệp và dữ liệu dưới một hình thức thông qua Ajax không?

Dữ liệu jQuery, Ajax và html

$("form#data").submit(function(){

    var formData = $(this).serialize();

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });

    return false;
});

<form id="data" method="post">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <button>Submit</button>
</form>

Tập tin jQuery, Ajax và html

$("form#files").submit(function(){

    var formData = new FormData($(this)[0]);

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        async: false,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });

    return false;
});

<form id="files" method="post" enctype="multipart/form-data">
    <input name="image" type="file" />
    <button>Submit</button>
</form>

Làm cách nào tôi có thể kết hợp những điều trên để tôi có thể gửi dữ liệu và tệp ở một dạng thông qua Ajax?

Mục tiêu của tôi là có thể gửi tất cả các biểu mẫu này trong một bài đăng với Ajax, điều đó có thể không?

<form id="datafiles" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>

2
Cách FormDatatiếp cận sẽ hoạt động tốt với các biểu mẫu có chứa bất cứ điều gì bạn muốn, không chỉ các trường tải lên tệp; nó không được hỗ trợ rộng rãi
lanzz

@lanzz mà mặc dù? một cái có serialization dường như chỉ hoạt động cho dữ liệu nhưng cái kia dường như chỉ hoạt động cho các tập tin?
Dan

Đánh giá bởi trang MDN này , tất cả dữ liệu biểu mẫu phải được gửi khi bạn sử dụngFormData
lanzz

1
@lanzz bạn nói đúng, nó hoạt động theo cách tôi nghĩ rằng tôi nên sử dụng id mẫu sai, bạn có thể tải lên cả tệp và dữ liệu thông qua một biểu mẫu với ajax.
Dan

Điều này dường như không hoạt động khi có đầu vào tập tin đa lựa chọn. Nó chỉ tải lên tập tin đầu tiên.
Sami Al-Subhi

Câu trả lời:


458

Vấn đề tôi gặp phải là sử dụng định danh jQuery sai.

Bạn có thể tải lên dữ liệu và tệp bằng một hình thức bằng cách sử dụng ajax .

PHP + HTML

<?php

print_r($_POST);
print_r($_FILES);
?>

<form id="data" method="post" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button>Submit</button>
</form>

jQuery + Ajax

$("form#data").submit(function(e) {
    e.preventDefault();    
    var formData = new FormData(this);

    $.ajax({
        url: window.location.pathname,
        type: 'POST',
        data: formData,
        success: function (data) {
            alert(data)
        },
        cache: false,
        contentType: false,
        processData: false
    });
});

Phiên bản ngắn

$("form#data").submit(function(e) {
    e.preventDefault();
    var formData = new FormData(this);    

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});

17
trong các phiên bản IE <10, giải pháp này sẽ không hoạt động, vì FormData là một đối tượng HTML5, không có trong IE 8 hoặc 9.
Xavier Guzman

34
$(this)[0]chỉ là một bí danh của this, vì vậy new FormData(this)nên là đủ.
r3wt

9
Dường như không thể kiểm tra Đối tượng FormData, hãy xem câu hỏi này (đối với bất kỳ ai chạy vào tình trạng không biết gì giống như tôi đã làm vì Đối tượng luôn trống rỗng).
Laura

28
Đối với độc giả trong tương lai: các khai báo contentType và processData rất quan trọng. Xem câu trả lời này để biết thêm thông tin.
AaronSieb

5
điều async: falsenày dường như không cần thiết để điều này hoạt động và gây ra sự chặn trên các trình duyệt di động (đơn luồng)
Jeremy Daalder

34

một tùy chọn khác là sử dụng iframe và đặt mục tiêu của biểu mẫu cho nó.

bạn có thể thử cái này (nó sử dụng jQuery):

function ajax_form($form, on_complete)
{
    var iframe;

    if (!$form.attr('target'))
    {
        //create a unique iframe for the form
        iframe = $("<iframe></iframe>").attr('name', 'ajax_form_' + Math.floor(Math.random() * 999999)).hide().appendTo($('body'));
        $form.attr('target', iframe.attr('name'));
    }

    if (on_complete)
    {
        iframe = iframe || $('iframe[name="' + $form.attr('target') + '"]');
        iframe.load(function ()
        {
            //get the server response
            var response = iframe.contents().find('body').text();
            on_complete(response);
        });
    }
}

nó hoạt động tốt với tất cả các trình duyệt, bạn không cần phải tuần tự hóa hoặc chuẩn bị dữ liệu. một nhược điểm là bạn không thể theo dõi tiến trình.

ngoài ra, ít nhất là đối với chrome, yêu cầu sẽ không xuất hiện trong tab "xhr" của các công cụ dành cho nhà phát triển mà dưới "doc"


1
Quả thực đó không phải là Ajax, vẫn có thể hữu ích với những người có cùng câu hỏi.
Roey

3
Tôi chỉ không thể tin được tại sao câu trả lời này nhận được -2, cuối cùng tôi đã sử dụng nó khi tôi cần Trình duyệt kế thừa Hỗ trợ
Sijav

Câu trả lời này phải nằm trong chuỗi vì các câu trả lời khác ghi chú 'trình duyệt cũ hơn không hoạt động' hoặc 'iframe hack có thể được sử dụng' nhưng không bao giờ giải quyết chúng. Đoạn mã đẹp, cũng cho thấy cách sử dụng onload chính xác +1
mschr

18

Tôi đã gặp vấn đề tương tự trong ASP.Net MVC với HttpPostedFilebase và thay vì sử dụng biểu mẫu trên Gửi tôi cần sử dụng nút bấm khi tôi cần làm một số thứ và sau đó nếu tất cả OK thì biểu mẫu gửi nên đây là cách tôi làm cho nó hoạt động

$(".submitbtn").on("click", function(e) {

    var form = $("#Form");

    // you can't pass Jquery form it has to be javascript form object
    var formData = new FormData(form[0]);

    //if you only need to upload files then 
    //Grab the File upload control and append each file manually to FormData
    //var files = form.find("#fileupload")[0].files;

    //$.each(files, function() {
    //  var file = $(this);
    //  formData.append(file[0].name, file[0]);
    //});

    if ($(form).valid()) {
        $.ajax({
            type: "POST",
            url: $(form).prop("action"),
            //dataType: 'json', //not sure but works for me without this
            data: formData,
            contentType: false, //this is requireded please see answers above
            processData: false, //this is requireded please see answers above
            //cache: false, //not sure but works for me without this
            error   : ErrorHandler,
            success : successHandler
        });
    }
});

điều này sẽ điền chính xác mô hình MVC của bạn, vui lòng đảm bảo trong Mô hình của bạn, Thuộc tính cho HttpPostedFileBase [] có cùng tên với Tên của điều khiển đầu vào trong html tức là

<input id="fileupload" type="file" name="UploadedFiles" multiple>

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}

1
Bạn là người tiết kiệm thời gian. :)
Suhail Mumtaz Awan

Trong trường hợp của tôi, tôi đã phải sử dụng:contentType : "application/octet-stream"
Barshe Roussy

Cảm ơn cậu! Bạn đã tiết kiệm rất nhiều thời gian.
Truy cập bị từ chối

Nó hoạt động với Django, tốt đẹp!
csandreas1

Cảm ơn bạn đời! 2 dòng dưới đây làm việc cho tôi. var form = $ ("# Mẫu"); var formData = new FormData (mẫu [0]);
Rajiv Kumar

15

Hoặc ngắn hơn:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});

Vì vậy, với điều này, làm thế nào để bạn xác nhận trường dữ liệu bằng cách sử dụng cùng một tập lệnh, đó là nếu bạn có trường văn bản và trường tệp trong biểu mẫu của mình
George

6

Đối với tôi, nó không hoạt động mà không có enctype: 'multipart/form-data'trường trong yêu cầu Ajax. Tôi hy vọng nó sẽ giúp được ai đó đang mắc kẹt trong một vấn đề tương tự.

Mặc dù enctype đã được đặt trong thuộc tính biểu mẫu , vì một số lý do, yêu cầu Ajax không tự động xác định enctypekhai báo không có khai báo rõ ràng (jQuery 3.3.1).

// Tested, this works for me (jQuery 3.3.1)

fileUploadForm.submit(function (e) {   
    e.preventDefault();
    $.ajax({
            type: 'POST',
            url: $(this).attr('action'),
            enctype: 'multipart/form-data',
            data: new FormData(this),
            processData: false,
            contentType: false,
            success: function (data) {
                console.log('Thank God it worked!');
            }
        }
    );
});

// enctype field was set in the form but Ajax request didn't set it by default.

<form action="process/file-upload" enctype="multipart/form-data" method="post" >

     <input type="file" name="input-file" accept="text/plain" required> 
     ...
</form>

Như những người khác đã đề cập ở trên, xin vui lòng đặc biệt chú ý đến các lĩnh vực contentTypeprocessData.


1
"Đối với tôi, nó không hoạt động mà không có mã hóa: trường 'nhiều dữ liệu / biểu mẫu dữ liệu' trong yêu cầu Ajax." - Điều đó không thể có tác dụng. Nó không phải là một thuộc tính được công nhận bởi jQuery.ajax. Xem các tài liệuenctypekhông được đề cập ở tất cả.
Quentin

Giống như tôi đã đề cập trước đây, tôi đã thử nhiều câu trả lời khác nhau nhưng chúng không hoạt động. Một lỗi Ajax cho biết lỗi mã hóa đã được hiển thị trong bảng điều khiển JS. Sau đó tôi đã làm theo hướng dẫn này cuối cùng đã làm cho mã của tôi hoạt động và tôi đã đăng nó ở đây. Có lẽ, enctypelĩnh vực không được đề cập trong tài liệu cho một lý do. Tôi chưa kiểm tra mã nguồn jQuery vì vậy tôi không thể nói chắc chắn.
Adithya Upadhya

"Có lẽ, trường enctype không được đề cập trong tài liệu vì một lý do." - Lý do đó là jQuery không làm gì với nó nên nó vô nghĩa.
Quentin

Có lẽ bạn có thể liên lạc với Mkyong và nói chuyện với anh ta. Tôi đã kiểm tra lại mã của mình bằng cách xóa enctypetrường và nó không còn tải lên các tệp (trả về lỗi loại mã hóa). Tôi không chắc nó hoạt động như thế nào vì tôi chưa kiểm tra mã nguồn jQuery. Tôi đã đăng câu trả lời này với mục đích giúp đỡ những người khác đang mắc kẹt trong một vấn đề tương tự. Tôi không câu cá để nâng cấp ở đây ... Nếu bạn có thêm câu hỏi / nhận xét, hãy trò chuyện thay vì bình luận.
Adithya Upadhya

1

Đối với tôi sau khi làm việc mã

$(function () {
    debugger;
    document.getElementById("FormId").addEventListener("submit", function (e) {
        debugger;
        if (ValidDateFrom()) { // Check Validation 
            var form = e.target;
            if (form.getAttribute("enctype") === "multipart/form-data") {
                debugger;
                if (form.dataset.ajax) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                    var xhr = new XMLHttpRequest();
                    xhr.open(form.method, form.action);
                    xhr.onreadystatechange = function (result) {
                        debugger;
                        if (xhr.readyState == 4 && xhr.status == 200) {
                            debugger;
                            var responseData = JSON.parse(xhr.responseText);
                            SuccessMethod(responseData); // Redirect to your Success method 
                        }
                    };
                    xhr.send(new FormData(form));
                }
            }
        }
    }, true);
});

Trong Phương thức đăng bài hành động của bạn, chuyển tham số dưới dạng HTTPPostedFileBase UploadFile và đảm bảo rằng tệp đầu vào của bạn giống như được đề cập trong tham số của Phương thức hành động. Nó cũng hoạt động với biểu mẫu AJAX Begin.

Hãy nhớ ở đây rằng Mẫu AJAX BEGIN của bạn sẽ không hoạt động ở đây vì bạn thực hiện cuộc gọi bài đăng của mình được xác định trong mã được đề cập ở trên và bạn có thể tham chiếu phương thức của mình trong mã theo Yêu cầu

Tôi biết tôi đang trả lời muộn nhưng đây là những gì làm việc cho tôi


1

Một cách đơn giản nhưng hiệu quả hơn:
new FormData()chính nó giống như một hộp đựng (hoặc một cái túi). Bạn có thể đặt mọi thứ attr hoặc tập tin vào chính nó. Điều duy nhất bạn sẽ cần phải thêm vào attribute, file, fileNameví dụ:

let formData = new FormData()
formData.append('input', input.files[0], input.files[0].name)

và chỉ cần vượt qua nó trong yêu cầu AJAX. Ví dụ:

    let formData = new FormData()
    var d = $('#fileid')[0].files[0]

    formData.append('fileid', d);
    formData.append('inputname', value);

    $.ajax({
        url: '/yourroute',
        method: 'POST',
        contentType: false,
        processData: false,
        data: formData,
        success: function(res){
            console.log('successfully')
        },
        error: function(){
            console.log('error')
        }
    })

Bạn có thể nối thêm n số tệp hoặc dữ liệu với FormData.

và nếu bạn đang thực hiện Yêu cầu AJAX từ tệp Script.js sang tệp Tuyến đường trong Node.js hãy cẩn thận khi sử dụng
req.bodyđể truy cập dữ liệu (tức là văn bản)
req.filesđể truy cập tệp (ví dụ: hình ảnh, video, v.v.)


-1

Trong trường hợp của tôi, tôi đã phải thực hiện một yêu cầu POST, trong đó có thông tin được gửi qua tiêu đề và cũng là một tệp được gửi bằng đối tượng FormData.

Tôi đã làm cho nó hoạt động bằng cách sử dụng kết hợp một số câu trả lời ở đây, vì vậy về cơ bản những gì kết thúc hoạt động là có năm dòng này trong yêu cầu Ajax của tôi:

 contentType: "application/octet-stream",
 enctype: 'multipart/form-data',
 contentType: false,
 processData: false,
 data: formData,

Trong đó formData là một biến được tạo như thế này:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);

1
contentType: "application/octet-stream",là có hại tích cực và lý do duy nhất nó không gây ra vấn đề là vì bạn đã ghi đè lên hai dòng sau đó.
Quentin

1
enctype: 'multipart/form-data',là vô nghĩa. jQuery.ajax không nhận ra paramater đó.
Quentin

Phần còn lại của câu trả lời của bạn không bao gồm bit "dữ liệu" của "dữ liệu tệp" từ tiêu đề câu hỏi.
Quentin

-2
<form id="form" method="post" action="otherpage.php" enctype="multipart/form-data">
    <input type="text" name="first" value="Bob" />
    <input type="text" name="middle" value="James" />
    <input type="text" name="last" value="Smith" />
    <input name="image" type="file" />
    <button type='button' id='submit_btn'>Submit</button>
</form>

<script>
$(document).on("click", "#submit_btn", function (e) {
    //Prevent Instant Click  
    e.preventDefault();
    // Create an FormData object 
    var formData = $("#form").submit(function (e) {
        return;
    });
    //formData[0] contain form data only 
    // You can directly make object via using form id but it require all ajax operation inside $("form").submit(<!-- Ajax Here   -->)
    var formData = new FormData(formData[0]);
    $.ajax({
        url: $('#form').attr('action'),
        type: 'POST',
        data: formData,
        success: function (response) {
            console.log(response);
        },
        contentType: false,
        processData: false,
        cache: false
    });
    return false;
});
</script>

///// otherpage.php

<?php
    print_r($_FILES);
?>
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.