อัพโหลดทั้งข้อมูลและไฟล์ในรูปแบบเดียวโดยใช้ Ajax?


384

ฉันใช้ jQuery และ Ajax สำหรับฟอร์มของฉันในการส่งข้อมูลและไฟล์ แต่ฉันไม่แน่ใจว่าจะส่งทั้งข้อมูลและไฟล์ในรูปแบบเดียวได้อย่างไร

ขณะนี้ฉันทำเกือบเหมือนกันทั้งสองวิธี แต่วิธีการรวบรวมข้อมูลในอาเรย์นั้นแตกต่างกันข้อมูลที่ใช้.serialize();แต่ไฟล์ใช้= new FormData($(this)[0]);

เป็นไปได้หรือไม่ที่จะรวมทั้งสองวิธีเพื่อให้สามารถอัพโหลดไฟล์และข้อมูลในรูปแบบเดียวผ่าน Ajax ได้?

Data jQuery, Ajax และ 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>

ไฟล์ jQuery, Ajax และ 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>

ฉันจะรวมข้างต้นเพื่อให้สามารถส่งข้อมูลและไฟล์ในรูปแบบเดียวผ่าน Ajax ได้อย่างไร

เป้าหมายของฉันคือการส่งแบบฟอร์มทั้งหมดนี้ในโพสต์เดียวกับ Ajax เป็นไปได้หรือไม่

<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
FormDataวิธีการควรปรับการทำงานที่มีรูปแบบที่มีสิ่งที่คุณต้องการไม่ได้เป็นเพียงเขตการอัปโหลดไฟล์; ไม่รองรับอย่างกว้างขวางว่า
lanzz

@lanzz ไหน หนึ่งที่มีซีเรียลไลซ์ดูเหมือนว่าจะใช้งานได้กับข้อมูลเท่านั้น
Dan

ตัดสินจากหน้า MDN นี้ควรส่งข้อมูลฟอร์มทั้งหมดเมื่อคุณใช้FormData
lanzz

1
@lanzz คุณพูดถูกมันทำงานอย่างที่ฉันคิดว่าฉันควรจะใช้รหัสประจำตัวที่ไม่ถูกต้องคุณสามารถอัปโหลดทั้งไฟล์และข้อมูลผ่านแบบฟอร์มเดียวด้วย ajax
Dan

สิ่งนี้ดูเหมือนจะไม่ทำงานเมื่อมีอินพุตไฟล์แบบหลายตัวเลือก มันอัปโหลดไฟล์แรกเท่านั้น
Sami Al-Subhi

คำตอบ:


458

ปัญหาที่ฉันมีคือการใช้ตัวระบุ jQuery ที่ไม่ถูกต้อง

คุณสามารถอัปโหลดข้อมูลและไฟล์ที่มีรูปแบบหนึ่งโดยใช้ 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
    });
});

เวอร์ชั่นสั้น

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

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

17
ในเวอร์ชันของ IE <10 โซลูชันนี้จะไม่ทำงานเนื่องจาก FormData เป็นวัตถุ HTML5 ซึ่งไม่ปรากฏใน IE 8 หรือ 9
Xavier Guzman

34
$(this)[0]เป็นเพียงนามแฝงของthisดังนั้นnew FormData(this)ควรมีเพียงพอ
r3wt

9
ดูเหมือนว่ามันเป็นไปไม่ได้ที่จะตรวจสอบ FormData Object ดูคำถามนี้ (สำหรับทุกคนที่ทำงานในความไร้เดียงสาเช่นเดียวกับที่ฉันทำเพราะวัตถุนั้นว่างเปล่าเสมอ)
ลอร่า

28
สำหรับผู้อ่านในอนาคต: การประกาศ contentType และ processData มีความสำคัญ ดูคำตอบนี้สำหรับข้อมูลเพิ่มเติม
AaronSieb

5
async: falseดูเหมือนจะไม่จำเป็นสำหรับการทำงานและทำให้เกิดการปิดกั้น (เกลียวเดี่ยว) เบราว์เซอร์มือถือ
เจเรมี Daalder

33

อีกตัวเลือกหนึ่งคือใช้ iframe และตั้งค่าเป้าหมายของฟอร์ม

คุณอาจลองสิ่งนี้ (ใช้ 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);
        });
    }
}

มันทำงานได้ดีกับเบราว์เซอร์ทั้งหมดคุณไม่จำเป็นต้องทำให้เป็นอนุกรมหรือเตรียมข้อมูล ด้านหนึ่งคือคุณไม่สามารถติดตามความคืบหน้าได้

นอกจากนี้อย่างน้อยสำหรับ Chrome คำขอจะไม่ปรากฏในแท็บ "xhr" ของเครื่องมือสำหรับนักพัฒนา แต่อยู่ภายใต้ "doc"


1
แน่นอนมันไม่ใช่อาแจ็กซ์ แต่ก็ยังมีประโยชน์สำหรับผู้ที่มีคำถามเดียวกัน
Roey

3
ฉันไม่อยากจะเชื่อเลยว่าทำไมคำตอบนี้ถึง -2 ฉันลงเอยด้วยการใช้สิ่งนี้เพราะฉันต้องการเบราว์เซอร์รุ่นเก่าการสนับสนุน
Sijav

คำตอบนี้ควรอยู่ในเธรดเนื่องจากคำตอบอื่น ๆ หมายเหตุ 'เบราว์เซอร์ที่เก่ากว่าไม่ทำงาน' หรือ 'แฮ็ก iframe สามารถใช้งานได้' แต่ไม่เคยพูดถึงมัน ส่วนของโค้ดที่ดีแสดงวิธีการใช้งาน onload อย่างถูกต้อง +1
mschr

18

ฉันมีปัญหาเดียวกันนี้ใน ASP.Net MVC กับ HttpPostedFilebase และแทนที่จะใช้แบบฟอร์มในการส่งฉันจำเป็นต้องใช้ปุ่มคลิกที่ฉันต้องทำบางสิ่งและถ้าทั้งหมดตกลงแบบฟอร์มการส่งดังนั้นนี่คือวิธีที่ฉันได้ทำงาน

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

สิ่งนี้จะเติมข้อมูลโมเดล MVC ของคุณอย่างถูกต้องโปรดตรวจสอบให้แน่ใจในแบบจำลองของคุณคุณสมบัติสำหรับ HttpPostedFileBase [] มีชื่อเดียวกับชื่อของตัวควบคุมอินพุตใน html เช่น

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

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

1
คุณเป็นคนประหยัดเวลา :)
Suhail Mumtaz Awan

ในกรณีของฉันฉันต้องใช้:contentType : "application/octet-stream"
Christophe Roussy

ขอบคุณเพื่อน! คุณประหยัดเวลาได้มาก
ปฏิเสธการเข้าถึง

มันทำงานร่วมกับ Django ได้ดี!
csandreas1

ขอบคุณเพื่อน! 2 บรรทัดด้านล่างใช้ได้สำหรับฉัน var form = $ ("# Form"); var formData = FormData ใหม่ (แบบฟอร์ม [0]);
Rajiv Kumar

15

หรือสั้นกว่า:

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

ดังนั้นด้วยวิธีนี้คุณจะตรวจสอบความถูกต้องของเขตข้อมูลโดยใช้สคริปต์เดียวกันนั่นคือถ้าคุณมีเขตข้อมูลข้อความและเขตข้อมูลไฟล์ในรูปแบบของคุณ
George

6

สำหรับฉันมันไม่ทำงานถ้าไม่มี enctype: 'multipart/form-data'ฟิลด์ในคำขอ Ajax ฉันหวังว่ามันจะช่วยให้คนที่ติดอยู่ในปัญหาที่คล้ายกัน

แม้ว่าจะenctype ถูกตั้งค่าไว้แล้วในแอตทริบิวต์ formด้วยเหตุผลบางอย่างคำขอ Ajax ก็ไม่ได้ระบุโดยอัตโนมัติenctypeโดยไม่มีการประกาศอย่างชัดเจน (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>

ดังที่คนอื่น ๆ ได้กล่าวไว้ข้างต้นโปรดใส่ใจเป็นพิเศษกับฟิลด์contentTypeและprocessData


1
"สำหรับฉันมันไม่ทำงานหากไม่มีประเภท: ฟิลด์ 'multipart / form-data' ในคำขอ Ajax" - นั่นไม่มีผลอะไรเลย ไม่ใช่คุณสมบัติที่รู้จักโดย jQuery.ajax ดูเอกสารที่enctypeไม่ได้กล่าวถึงทั้งหมด
เควนติน

อย่างที่ฉันพูดถึงก่อนหน้านี้ฉันพยายามตอบหลายคำตอบต่างกัน แต่พวกเขาไม่สามารถทำงานได้ ข้อผิดพลาด Ajax ที่ระบุข้อผิดพลาดการเข้ารหัสถูกแสดงในคอนโซล JS ต่อมาฉันติดตามการสอนนี้ซึ่งทำให้รหัสของฉันทำงานได้ในที่สุด บางทีenctypeฟิลด์อาจไม่ครอบคลุมในเอกสารด้วยเหตุผล ฉันไม่ได้ตรวจสอบซอร์สโค้ด jQuery ดังนั้นฉันไม่สามารถพูดด้วยความมั่นใจ
Adithya Upadhya

"บางทีฟิลด์ enctype ไม่ได้กล่าวถึงในเอกสารประกอบด้วยเหตุผล" - เหตุผลนั้นคือ jQuery ไม่ได้ทำอะไรกับมันเลยมันไร้สาระ
Quentin

บางทีคุณสามารถติดต่อ Mkyongและคุยกับเขาแทนได้ ฉันทดสอบโค้ดอีกครั้งโดยลบenctypeฟิลด์และไม่อัปโหลดไฟล์อีกต่อไป (การส่งคืนข้อผิดพลาดประเภทการเข้ารหัส) ฉันไม่แน่ใจว่าวิธีการทำงานตั้งแต่ฉันไม่ได้ตรวจสอบซอร์สโค้ด jQuery ฉันโพสต์คำตอบนี้ด้วยความตั้งใจที่จะช่วยเหลือผู้อื่นที่ติดอยู่ในปัญหาที่คล้ายกัน ฉันไม่ได้ตกปลาเพื่อโหวตที่นี่ ... หากคุณมีคำถาม / ความคิดเห็นเพิ่มเติมลองแชทแทนที่จะแสดงความคิดเห็น
Adithya Upadhya

1

สำหรับฉันทำงานรหัสต่อไปนี้

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

ในวิธีการโพสต์แอคชันของคุณให้ส่งพารามิเตอร์เป็น HttpPostedFileBase UploadFile และตรวจสอบให้แน่ใจว่าอินพุตไฟล์ของคุณมีค่าเหมือนกับที่กล่าวไว้ในพารามิเตอร์ของวิธีการดำเนินการ มันควรทำงานกับ AJAX Begin form เช่นกัน

จำไว้ที่นี่ว่าแบบฟอร์ม AJAX BEGIN ของคุณจะไม่ทำงานที่นี่เนื่องจากคุณทำการโทรโพสต์ของคุณในรหัสที่กล่าวถึงข้างต้นและคุณสามารถอ้างอิงวิธีการของคุณในรหัสตามความต้องการ

ฉันรู้ว่าฉันกำลังตอบช้า แต่นี่เป็นสิ่งที่ใช้ได้ผลสำหรับฉัน


1

วิธีที่เรียบง่าย แต่มีประสิทธิภาพมากกว่า:
new FormData()ตัวมันเองเหมือนภาชนะ (หรือถุง) คุณสามารถใส่ทุกอย่างในไฟล์หรือ attr สิ่งเดียวที่คุณจะต้องต่อท้ายattribute, file, fileNameเช่น:

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

และผ่านมันไปในคำขอ AJAX เช่น:

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

คุณสามารถผนวก n จำนวนไฟล์หรือข้อมูลด้วย FormData

และหากคุณกำลังร้องขอ AJAX จากไฟล์ Script.js ไปยังไฟล์ Route ใน Node.js ระวังการใช้
req.bodyเพื่อเข้าถึงข้อมูล (เช่นข้อความ)
req.filesเพื่อเข้าถึงไฟล์ (เช่นรูปภาพวิดีโอ ฯลฯ )


-1

ในกรณีของฉันฉันต้องทำการร้องขอ POST ซึ่งมีข้อมูลที่ส่งผ่านส่วนหัวและไฟล์ที่ส่งโดยใช้วัตถุ FormData

ฉันทำให้มันทำงานโดยใช้การรวมกันของคำตอบที่นี่ดังนั้นโดยทั่วไปสิ่งที่สิ้นสุดการทำงานคือการมีห้าบรรทัดในคำขอ Ajax ของฉัน:

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

โดยที่ formData เป็นตัวแปรที่สร้างเช่นนี้

 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",เป็นอันตรายอย่างแข็งขันและสาเหตุเดียวที่ทำให้เกิดปัญหาไม่ได้คือเพราะคุณเขียนทับมันสองบรรทัดในภายหลัง
เควนติน

1
enctype: 'multipart/form-data',ไม่มีจุดหมาย jQuery.ajax ไม่รู้จักพารามิเตอร์เหล่านั้น
เควนติน

…คำตอบที่เหลือของคุณไม่สามารถครอบคลุม "data" bit of "data and files" จากชื่อคำถาม
เควนติน

-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);
?>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.