Przesyłasz zarówno dane, jak i pliki w jednej formie za pomocą Ajax?

384

Używam jQuery i Ajax do formularzy do przesyłania danych i plików, ale nie jestem pewien, jak wysłać zarówno dane, jak i pliki w jednym formularzu?

Obecnie robię prawie to samo z obiema metodami, ale sposób, w jaki dane są gromadzone w tablicy, jest inny, dane używają, .serialize();ale pliki używają= new FormData($(this)[0]);

Czy można połączyć obie metody, aby móc przesyłać pliki i dane w jednej formie za pośrednictwem Ajax?

Dane jQuery, Ajax i 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>

Pliki jQuery, Ajax i 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>

Jak połączyć powyższe, aby móc wysyłać dane i pliki w jednej formie za pośrednictwem Ajax?

Moim celem jest, aby móc wysłać cały ten formularz w jednym poście z Ajax, czy to możliwe?

<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>
Dan
źródło
2
FormDataPodejście powinno działać dobrze z form, które zawierają co chcesz, nie tylko pola Prześlij plik; nie jest to jednak powszechnie obsługiwane.
lanzz
@lanzz które jednak? ten z serializacją wydaje się działać tylko dla danych, ale drugi wydaje się działać tylko dla plików?
Dan
Sądząc po tej stronie MDN , wszystkie dane formularza należy przesłać podczas korzystaniaFormData
lanzz
1
@lanzz masz rację, działa tak, jak myślałem, że powinienem używać niewłaściwego identyfikatora formularza, możesz przesyłać zarówno pliki, jak i dane za pośrednictwem jednego formularza za pomocą ajax.
Dan
Wydaje się, że to nie działa, gdy istnieje plik z wielokrotnym wyborem pliku. Przesyła tylko pierwszy plik.
Sami Al-Subhi,

Odpowiedzi:

458

Problem, który miałem, to użycie niewłaściwego identyfikatora jQuery.

Możesz przesyłać dane i pliki w jednym formularzu za pomocą 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
    });
});

Krótka wersja

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

    $.post($(this).attr("action"), formData, function(data) {
        alert(data);
    });
});
Dan
źródło
17
w wersjach IE <10 to rozwiązanie nie będzie działać, ponieważ FormData jest obiektem HTML5, nieobecnym w IE 8 lub 9.
Xavier Guzman
34
$(this)[0]jest tylko aliasem this, więc new FormData(this)powinno wystarczyć.
r3wt,
9
Wydaje się, że nie jest możliwe sprawdzenie obiektu FormData, zobacz to pytanie (dla każdego, kto ma taką samą bezradność jak ja, ponieważ obiekt był zawsze pusty).
Laura,
28
Dla przyszłych czytelników: deklaracje contentType i processData są ważne. Zobacz tę odpowiedź, aby uzyskać więcej informacji.
AaronSieb
5
async: falsewydaje się, że nie jest to wymagane do działania i powoduje blokowanie w mobilnych przeglądarkach (jednowątkowych)
Jeremy Daalder
33

inną opcją jest użycie elementu iframe i ustawienie dla niego celu formularza.

możesz spróbować tego (używa 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);
        });
    }
}

działa dobrze ze wszystkimi przeglądarkami, nie trzeba serializować ani przygotowywać danych. jedną wadą jest to, że nie można monitorować postępów.

także, przynajmniej w przypadku Chrome, żądanie nie pojawi się na karcie „xhr” narzędzi programistycznych, ale w „doc”

Roey
źródło
1
Rzeczywiście nie jest to Ajax, ale może być przydatne dla osób z tym samym pytaniem.
Roey,
3
Po prostu nie mogę uwierzyć, dlaczego ta odpowiedź dostaje -2, ostatecznie skorzystałem z niej, ponieważ potrzebowałem pomocy technicznej dla starszych przeglądarek
Sijav
Ta odpowiedź powinna znajdować się w wątku, ponieważ inne odpowiedzi wskazują, że „starsze przeglądarki nie działają” lub „można użyć hakowania iframe”, ale nigdy się do nich nie odnosi. Niezły kawałek kodu, pokazujący także, jak poprawnie używać onload +1
mschr
18

Miałem ten sam problem w ASP.Net MVC z HttpPostedFilebase i zamiast używać formularza w Prześlij, musiałem użyć przycisku po kliknięciu w miejscu, w którym musiałem zrobić kilka rzeczy, a następnie, jeśli wszystko w porządku, prześlij formularz, więc oto jak to działa

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

spowoduje to prawidłowe wypełnienie modelu MVC, upewnij się, że w modelu właściwość HttpPostedFileBase [] ma taką samą nazwę jak nazwa kontrolki wejściowej w html, tj.

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

public class MyViewModel
{
    public HttpPostedFileBase[] UploadedFiles { get; set; }
}
h_power11
źródło
1
Oszczędzasz czas. :)
Suhail Mumtaz Awan,
W moim przypadku musiałem użyć:contentType : "application/octet-stream"
Christophe Roussy
Dzięki stary! Zaoszczędziłeś dużo czasu.
Odmowa dostępu
Działa z Django, miło!
csandreas1
Dzięki stary! Poniższe 2 wiersze działały dla mnie. var form = $ („# Form”); var formData = new FormData (formularz [0]);
Rajiv Kumar
15

Lub krócej:

$("form#data").submit(function() {
    var formData = new FormData(this);
    $.post($(this).attr("action"), formData, function() {
        // success    
    });
    return false;
});
schaenk
źródło
w ten sposób, jak sprawdzić poprawność pola danych przy użyciu tego samego skryptu, to znaczy, jeśli masz pole tekstowe i pole pliku w formularzu
George
6

Dla mnie nie działało bez enctype: 'multipart/form-data'pola w żądaniu Ajax. Mam nadzieję, że pomoże to komuś, kto tkwi w podobnym problemie.

Chociaż z jakiegoś powodu enctype zostało już ustawione w atrybucie formularza , żądanie Ajax nie zidentyfikowało automatycznie enctypebez wyraźnej deklaracji (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>

Jak wspomniano powyżej, należy również zwrócić szczególną uwagę na pola contentTypei processData.

Adithya Upadhya
źródło
1
„Dla mnie nie działało bez pola enctype:„ multipart / form-data ”w żądaniu Ajax.” - To nie mogło mieć żadnego efektu. To nie jest właściwość rozpoznawana przez jQuery.ajax. Zobacz dokumentację, w której enctypew ogóle nie wspomniano.
Quentin
Jak wspomniałem wcześniej, próbowałem wielu różnych odpowiedzi, ale one nie działały. Błąd Ajax stwierdzający błąd kodowania został pokazany w konsoli JS. Później wykonałem ten samouczek, który w końcu sprawił, że mój kod zadziałał i opublikowałem go tutaj. Być może enctypez jakiegoś powodu pole nie jest uwzględnione w dokumentacji. Nie sprawdziłem kodu źródłowego jQuery, więc nie mogę powiedzieć z całą pewnością.
Adithya Upadhya
„Być może z jakiegoś powodu pole kodu nie jest uwzględnione w dokumentacji”. - Powodem jest to, że jQuery nic z tym nie robi, więc to nonsens.
Quentin
Być może mógłbyś skontaktować się z Mkyongiem i zamiast tego porozmawiać z nim. Przetestowałem mój kod ponownie, usuwając enctypepole i nie przesyła on już plików (zwraca błąd typu kodowania). Nie jestem pewien, jak to działa, ponieważ nie sprawdziłem kodu źródłowego jQuery. Zamieściłem tę odpowiedź z zamiarem pomocy innym, którzy tkwią w podobnym problemie. Nie szukam tutaj głosów pozytywnych ... Jeśli masz dodatkowe pytania / komentarze, porozmawiajmy zamiast komentować.
Adithya Upadhya
1

Dla mnie następujący kod działa

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

W metodzie Action Post przekaż parametr jako HttpPostedFileBase UploadFile i upewnij się, że dane wejściowe pliku są takie same, jak wymienione w parametrze metody akcji. Powinien również współpracować z formularzem AJAX Begin.

Pamiętaj tutaj, że Twój formularz AJAX BEGIN nie będzie tutaj działał, ponieważ wykonujesz połączenie pocztowe zdefiniowane w powyższym kodzie i możesz odwoływać się do metody w kodzie zgodnie z wymaganiami

Wiem, że odpowiadam późno, ale to zadziałało dla mnie

Pranav Kulshrestha
źródło
1

Prosty, ale bardziej skuteczny sposób:
new FormData()sam w sobie jest jak pojemnik (lub torba). Możesz umieścić wszystko attr lub plik w sobie. Jedyne, czego potrzebujesz, to dołączyć attribute, file, fileNamenp .:

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

i po prostu przekaż go w żądaniu AJAX. Na przykład:

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

Możesz dodać n liczby plików lub danych za pomocą FormData.

a jeśli tworzysz żądanie AJAX z pliku Script.js do pliku trasy w Node.js, strzeż się korzystania
req.bodyz dostępu do danych (tj. tekstu) w
req.filescelu uzyskania dostępu do pliku (np. obrazu, wideo itp.)

kartik tyagi
źródło
-1

W moim przypadku musiałem złożyć żądanie POST, które zawierało informacje przesłane przez nagłówek, a także plik wysłany przy użyciu obiektu FormData.

Sprawiłem, że działało, używając kombinacji kilku odpowiedzi tutaj, więc w zasadzie to, co skończyło się działaniem, zawierało pięć wierszy w moim żądaniu Ajax:

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

Gdzie formData była zmienną utworzoną w ten sposób:

 var file = document.getElementById('uploadedFile').files[0];
 var form = $('form')[0];
 var formData = new FormData(form);
 formData.append("File", file);
ndarriulat
źródło
1
contentType: "application/octet-stream",jest aktywnie szkodliwy, a jedynym powodem, dla którego nie powoduje problemu, jest nadpisanie go dwie linie później.
Quentin
1
enctype: 'multipart/form-data',jest bezcelowe. jQuery.ajax nie rozpoznaje tego parametru.
Quentin
… Reszta twojej odpowiedzi nie obejmuje „danych” bitów „danych i plików” z tytułu pytania.
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);
?>
Shailesh Dwivedi
źródło