Integracja Dropzone.js z istniejącym formularzem HTML z innymi polami

181

Obecnie mam formularz HTML, w którym użytkownicy wpisują szczegóły ogłoszenia, które chcą zamieścić. Chcę teraz móc dodać strefę zrzutu do przesyłania zdjęć przedmiotu na sprzedaż.

Znalazłem Dropzone.js, który wydaje się robić większość tego, czego potrzebuję. Jednak patrząc do dokumentacji wydaje się, że należy określić klasę całego formularza jako dropzone(a nie tylko elementu wejściowego ). Oznacza to, że cała moja forma staje się strefą zrzutu .

Czy można użyć dropzone tylko w części mojego formularza, tj. Określając element tylko jako klasę „dropzone” , a nie cały formularz?

Mógłbym użyć oddzielnych formularzy, ale chcę, aby użytkownik mógł przesłać to wszystko za pomocą jednego przycisku.

Czy jest też inna biblioteka, która może to zrobić?

Wielkie dzięki

Ben Thompson
źródło

Odpowiedzi:

59

Oto inny sposób, aby to zrobić: dodaj do divformularza z nazwą klasy dropzone i zaimplementuj dropzone programowo.

HTML:

<div id="dZUpload" class="dropzone">
      <div class="dz-default dz-message"></div>
</div>

JQuery:

$(document).ready(function () {
    Dropzone.autoDiscover = false;
    $("#dZUpload").dropzone({
        url: "hn_SimpeFileUploader.ashx",
        addRemoveLinks: true,
        success: function (file, response) {
            var imgName = response;
            file.previewElement.classList.add("dz-success");
            console.log("Successfully uploaded :" + imgName);
        },
        error: function (file, response) {
            file.previewElement.classList.add("dz-error");
        }
    });
});

Uwaga: wyłączenie funkcji automatycznego wykrywania, w przeciwnym razie Dropzone spróbuje dołączyć dwukrotnie

Artykuł na blogu : Dropzone js + Asp.net: łatwy sposób na przesyłanie zdjęć zbiorczych

Satinder singh
źródło
25
Dzięki temu nie może użyć domyślnego przycisku przesyłania, nie odpowiada on na jego pytanie
Clement
5
ale to nadal nie wykorzystuje oryginalnego formularza do przesłania
dangel
3
to był mój problem i rozwiązałeś go, ty @Satindersingh
Su4p
1
@ Su4p: cieszę się, że ci to pomaga, możesz również sprawdzić link do artykułu na blogu, aby uzyskać szczegółowe wyjaśnienie wraz z opcją zmiany rozmiaru obrazu podczas przesyłania
Satinder singh
2
To bardzo pomogło, możesz ustawić dowolny element jako strefę zrzutu, jeśli ustawisz adres URL ręcznie. Użyłem programu obsługi sukcesu, aby opublikować nazwę pliku w ukrytym / wyłączonym polu w podstawowej formie.
DigitalDesignDj
40

Miałem dokładnie ten sam problem i stwierdziłem, że odpowiedź Varana Sinayee była jedyną, która faktycznie rozwiązała pierwotne pytanie. Tę odpowiedź można jednak uprościć, więc oto prostsza wersja.

Kroki są następujące:

  1. Utwórz normalny formularz (nie zapomnij o metodzie i argumentach typu enctype, ponieważ nie jest to już obsługiwane przez dropzone).

  2. Umieść element div w środku z class="dropzone"(tak dołącza się do niego Dropzone) i id="yourDropzoneName"(służy do zmiany opcji).

  3. Ustaw opcje Dropzone, aby ustawić adres URL, pod którym formularz i pliki zostaną opublikowane, dezaktywuj autoProcessQueue (więc dzieje się to tylko wtedy, gdy użytkownik naciśnie przycisk „Prześlij”) i zezwalaj na wielokrotne przesyłanie (jeśli tego potrzebujesz).

  4. Ustaw funkcję init, aby używała Dropzone zamiast domyślnego zachowania po kliknięciu przycisku przesyłania.

  5. Wciąż w funkcji init, użyj procedury obsługi zdarzeń „sendmultiple”, aby wysłać dane formularza wraz z plikami.

Voilà! Możesz teraz pobrać dane tak, jak w normalnym formularzu, w $ _POST i $ _FILES (w przykładzie stanie się to w pliku upload.php)

HTML

<form action="upload.php" enctype="multipart/form-data" method="POST">
    <input type="text" id ="firstname" name ="firstname" />
    <input type="text" id ="lastname" name ="lastname" />
    <div class="dropzone" id="myDropzone"></div>
    <button type="submit" id="submit-all"> upload </button>
</form>

JS

Dropzone.options.myDropzone= {
    url: 'upload.php',
    autoProcessQueue: false,
    uploadMultiple: true,
    parallelUploads: 5,
    maxFiles: 5,
    maxFilesize: 1,
    acceptedFiles: 'image/*',
    addRemoveLinks: true,
    init: function() {
        dzClosure = this; // Makes sure that 'this' is understood inside the functions below.

        // for Dropzone to process the queue (instead of default form behavior):
        document.getElementById("submit-all").addEventListener("click", function(e) {
            // Make sure that the form isn't actually being sent.
            e.preventDefault();
            e.stopPropagation();
            dzClosure.processQueue();
        });

        //send all the form data along with the files:
        this.on("sendingmultiple", function(data, xhr, formData) {
            formData.append("firstname", jQuery("#firstname").val());
            formData.append("lastname", jQuery("#lastname").val());
        });
    }
}
mrtnmgs
źródło
1
To rozwiązanie jest fajne i działa, ale nie przekierowuje już do następnej strony, ponieważ zapobiega domyślnemu zachowaniu przesyłania.
Felix G.
@TIIUNDER - jest przygotowany do wysyłania informacji o formularzu poprzez wywołanie Ajax bez przeładowywania strony - dlatego istnieje e.preventDefault ();
born2fr4g
1
@TIUNDER możesz dodać przekierowanie w przypadku sukcesu
doflamingo
Czy można przesłać formularz po processQueue()rozmowie telefonicznej? Próbuję użyć submit()lub click()oba nie działają.
Gray Li
1
+1 Wydaje się, że jest to jedyne działające rozwiązanie. Zamiast robić formData.append jeden po drugim, możesz też to zrobić $(":input[name]", $("form")).each(function () { formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); }); (przepraszam, nie wiem, jak tutaj
wstawić podziały wierszy
20

Najpopularniejszą biblioteką do przesyłania obrazów jest „dropzone.js”. Jeśli chcesz, aby plik „dropzone.js” był tylko częścią formularza, wykonaj następujące czynności:

1) po stronie klienta:

HTML:

    <form action="/" enctype="multipart/form-data" method="POST">
        <input type="text" id ="Username" name ="Username" />
        <div class="dropzone" id="my-dropzone" name="mainFileUploader">
            <div class="fallback">
                <input name="file" type="file" multiple />
            </div>
        </div>
    </form>
    <div>
        <button type="submit" id="submit-all"> upload </button>
    </div>

JQuery:

    <script>
        Dropzone.options.myDropzone = {
            url: "/Account/Create",
            autoProcessQueue: false,
            uploadMultiple: true,
            parallelUploads: 100,
            maxFiles: 100,
            acceptedFiles: "image/*",

            init: function () {

                var submitButton = document.querySelector("#submit-all");
                var wrapperThis = this;

                submitButton.addEventListener("click", function () {
                    wrapperThis.processQueue();
                });

                this.on("addedfile", function (file) {

                    // Create the remove button
                    var removeButton = Dropzone.createElement("<button class='btn btn-lg dark'>Remove File</button>");

                    // Listen to the click event
                    removeButton.addEventListener("click", function (e) {
                        // Make sure the button click doesn't submit the form:
                        e.preventDefault();
                        e.stopPropagation();

                        // Remove the file preview.
                        wrapperThis.removeFile(file);
                        // If you want to the delete the file on the server as well,
                        // you can do the AJAX request here.
                    });

                    // Add the button to the file preview element.
                    file.previewElement.appendChild(removeButton);
                });

                this.on('sendingmultiple', function (data, xhr, formData) {
                    formData.append("Username", $("#Username").val());
                });
            }
        };
    </script>

2) po stronie serwera:

ASP.Net MVC

    [HttpPost]
    public ActionResult Create()
    {
        var postedUsername = Request.Form["Username"].ToString();
        foreach (var imageFile in Request.Files)
        {

        }

        return Json(new { status = true, Message = "Account created." });
    }
Varan Sinayee
źródło
2
Dzięki za post! Rozwiązał mój problem. Kolejne szybkie pytanie, to nie działa, gdy nie wybrano żadnych zdjęć (do przesłania), jak to rozwiązać?
Sato
BTW: jeśli używasz akcji kontrolera z powiązaniem modelu i prześlesz swój formularz w ten sposób, model będzie pusty. Z jakiegoś powodu ta metoda nie wiąże rzeczywistych danych z modelem.
Edward Chopuryan,
1
kiedy autoProcessQueue = false, żadne zdarzenia nie są uruchamiane
cyril
@Sato po kliknięciu przycisku wysyłania możesz sprawdzić długość akceptowanych plików w strefie dropzone, używając galleryfile.getAcceptedFiles (). Length, a jeśli nie ma przesłanego pliku, prześlij swój formularz.
Varan Sinayee,
@EdwardChopuryan Nie ma to związku z metodą przesyłania danych przez dropzone. Prawdopodobnie problem dotyczy Twojej „konwencji nazewnictwa” tagów wejściowych na Twojej platformie, takich jak ASP.Net MVC.
Varan Sinayee,
11

Samouczek Enyo jest doskonały.

Zauważyłem, że przykładowy skrypt w samouczku działał dobrze w przypadku przycisku osadzonego w strefie dropzone (tj. Element formularza). Jeśli chcesz mieć przycisk poza elementem formularza, udało mi się to zrobić za pomocą zdarzenia click:

Po pierwsze, HTML:

<form id="my-awesome-dropzone" action="/upload" class="dropzone">  
    <div class="dropzone-previews"></div>
    <div class="fallback"> <!-- this is the fallback if JS isn't working -->
        <input name="file" type="file" multiple />
    </div>

</form>
<button type="submit" id="submit-all" class="btn btn-primary btn-xs">Upload the file</button>

Następnie tag script ....

Dropzone.options.myAwesomeDropzone = { // The camelized version of the ID of the form element

    // The configuration we've talked about above
    autoProcessQueue: false,
    uploadMultiple: true,
    parallelUploads: 25,
    maxFiles: 25,

    // The setting up of the dropzone
    init: function() {
        var myDropzone = this;

        // Here's the change from enyo's tutorial...

        $("#submit-all").click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            myDropzone.processQueue();
        }); 
    }
}
kablamus
źródło
23
Nie możesz mieć formularza wewnątrz formularza i przesłać.
paul
1
Kiedy próbuję tego, kontener podglądów dropzone wydaje się być ignorowany. Dropzone po prostu dodaje podglądy na dole formularza. Będziesz musiał dodać „previewsContainer:” .dropzone-previews ”,„ do swojej konfiguracji.
Aaron Hill
6
To nie odpowiada na pierwotne pytanie. Pierwotne pytanie dotyczyło sposobu korzystania z Dropzone z istniejącym formularzem, a nie lokalizacji przycisku wywołującego akcję.
CSSian
7

W związku z tym, co mówił sqram, Dropzone ma dodatkową nieudokumentowaną opcję „hiddenInputContainer”. Wszystko, co musisz zrobić, to ustawić tę opcję na selektor formularza, do którego chcesz dołączyć ukryte pole pliku. I voila! Pole pliku „.dz-hidden-input”, które Dropzone normalnie dodaje do treści, w magiczny sposób przenosi się do formularza. Bez zmiany kodu źródłowego Dropzone.

Teraz, gdy to działa, aby przenieść pole pliku Dropzone do formularza, pole nie ma nazwy. Musisz więc dodać:

_this.hiddenFileInput.setAttribute("name", "field_name[]");

do dropzone.js po tym wierszu:

_this.hiddenFileInput = document.createElement("input");

wokół linii 547.

Codedragon
źródło
5

Mam do tego bardziej zautomatyzowane rozwiązanie.

HTML:

<form role="form" enctype="multipart/form-data" action="{{ $url }}" method="{{ $method }}">
    {{ csrf_field() }}

    <!-- You can add extra form fields here -->

    <input hidden id="file" name="file"/>

    <!-- You can add extra form fields here -->

    <div class="dropzone dropzone-file-area" id="fileUpload">
        <div class="dz-default dz-message">
            <h3 class="sbold">Drop files here to upload</h3>
            <span>You can also click to open file browser</span>
        </div>
    </div>

    <!-- You can add extra form fields here -->

    <button type="submit">Submit</button>
</form>

JavaScript:

Dropzone.options.fileUpload = {
    url: 'blackHole.php',
    addRemoveLinks: true,
    accept: function(file) {
        let fileReader = new FileReader();

        fileReader.readAsDataURL(file);
        fileReader.onloadend = function() {

            let content = fileReader.result;
            $('#file').val(content);
            file.previewElement.classList.add("dz-success");
        }
        file.previewElement.classList.add("dz-complete");
    }
}

Laravel:

// Get file content
$file = base64_decode(request('file'));

Nie ma potrzeby wyłączania DropZone Discovery, a przesyłanie zwykłego formularza będzie mogło wysłać plik z innymi polami formularza za pośrednictwem standardowej serializacji formularza.

Ten mechanizm przechowuje zawartość pliku jako łańcuch base64 w ukrytym polu wejściowym, gdy jest on przetwarzany. Możesz go zdekodować z powrotem do łańcucha binarnego w PHP za pomocą standardowej base64_decode()metody.

Nie wiem, czy ta metoda zostanie naruszona w przypadku dużych plików, ale działa z plikami ~ 40 MB.

Umair Ahmed
źródło
W jaki sposób dekodujesz i przetwarzasz dane z innych pól, które zostaną przesłane wraz z obrazami?
sam
@sam Nie ma potrzeby dekodowania innych pól. W pierwszej kolejności nie są one kodowane, tylko plik zostaje zakodowany.
Umair Ahmed
Czy możesz udostępnić przykładowy kod dla html, javascript i jak pobrać w laravel php.
sam
2
Jeśli chcesz dodać wiele obrazów, musisz usunąć dane wejściowe pliku html i dodać je quing js dla każdego obrazu $ ('# fileUpload'). Append ('<input hidden name = "files []" value =' + content + ' /> '), gdzie zawartość jest obrazem zakodowanym w formacie base64.
AleXzpm
1
@codepushr cóż, to stara odpowiedź z czasów, gdy nie rozważaliśmy płatnych rozwiązań. Teraz kupiliśmy FileUploader, chociaż ma on swoje własne sztuczki, ale wystarczy powiedzieć, że można go dostosować do prawie wszystkiego.
Umair Ahmed
4

Możesz zmodyfikować formData, przechwytując zdarzenie „send” ze swojej strefy dropzone.

dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});
shawnrushefsky
źródło
1
Podoba mi się ta odpowiedź - ale zakładałaby, że nazwa pola i wartość zostały wypełnione. Jest to uruchamiane podczas przesyłania, które może nastąpić w innym momencie niż przesłanie formularza. Innymi słowy nie można zakładać, że wysyłając zdjęcie formularz jest wypełniony.
Antony
4

Aby przesłać wszystkie pliki wraz z innymi danymi formularza w jednym żądaniu, możesz skopiować tymczasowe ukryte inputwęzły Dropzone.js do swojego formularza. Możesz to zrobić w ramach addedfilesobsługi zdarzeń:

var myDropzone = new Dropzone("myDivSelector", { url: "#", autoProcessQueue: false });
myDropzone.on("addedfiles", () => {
  // Input node with selected files. It will be removed from document shortly in order to
  // give user ability to choose another set of files.
  var usedInput = myDropzone.hiddenFileInput;
  // Append it to form after stack become empty, because if you append it earlier
  // it will be removed from its parent node by Dropzone.js.
  setTimeout(() => {
    // myForm - is form node that you want to submit.
    myForm.appendChild(usedInput);
    // Set some unique name in order to submit data.
    usedInput.name = "foo";
  }, 0);
});

Oczywiście jest to obejście zależne od szczegółów implementacji. Powiązany kod źródłowy .

Leonid Vasilev
źródło
Zasadniczo zastosowałem to podejście, ale z powodu widocznych opóźnień przetwarzania ostatecznie podłączyłem przetwarzanie zawartości pliku pod myDropzone.on("thumbnail", () => {})zdarzeniem. Wykonywanie przetwarzania natychmiastowego "addedFile"pliku może nadal być dostępne undefinedpo uzyskaniu dostępu.
Matti
Próbuję tego użyć i działa to poprzez przeniesienie ukrytego pola wejściowego pliku do formularza, a kiedy przesyłam, dane posta pokazują moje pole, files[]ale jest puste bez względu na to, co robię. Jakieś pomysły? Robię to w Laravel, jeśli ma to znaczenie.
zen
Witaj! Dlaczego wybrany plik jest przesyłany, ale jeśli plik został upuszczony, to nie (błąd 4)?
Ingus
2

Chcę wnieść tutaj odpowiedź, ponieważ ja również napotkałem ten sam problem - chcemy, aby element $ _FILES był dostępny jako część tego samego postu jako inny formularz. Moja odpowiedź opiera się na @mrtnmgs, jednak zwraca uwagę na komentarze dodane do tego pytania.

Po pierwsze: Dropzone publikuje swoje dane za pośrednictwem AJAX

Tylko dlatego, że używasz tej formData.appendopcji, nadal oznacza to, że musisz zająć się działaniami UX - tj. To wszystko dzieje się za kulisami i nie jest typowym postem w formularzu. Dane są publikowane w Twoimurl parametru.

Po drugie: jeśli w związku z tym chcesz naśladować post formularza, będziesz musiał przechowywać opublikowane dane

Wymaga to kodu po stronie serwera do przechowywania $_POSTlub$_FILES w sesji, która jest dostępna dla użytkownika po załadowaniu innej strony, ponieważ użytkownik nie przejdzie do strony, na której odebrano opublikowane dane.

Po trzecie: musisz przekierować użytkownika na stronę, na której te dane są przetwarzane

Teraz, gdy opublikowałeś swoje dane, zapisałeś je w sesji, musisz wyświetlić / wykonać je dla użytkownika na dodatkowej stronie. Musisz również wysłać użytkownika do tej strony.

Więc dla mojego przykładu:

[Kod Dropzone: używa Jquery]

$('#dropArea').dropzone({
    url:        base_url+'admin/saveProject',
    maxFiles:   1,
    uploadMultiple: false,
    autoProcessQueue:false,
    addRemoveLinks: true,
    init:       function(){
        dzClosure = this;

        $('#projectActionBtn').on('click',function(e) {
            dzClosure.processQueue(); /* My button isn't a submit */
        });

        // My project only has 1 file hence not sendingmultiple
        dzClosure.on('sending', function(data, xhr, formData) {
            $('#add_user input[type="text"],#add_user textarea').each(function(){
                formData.append($(this).attr('name'),$(this).val());
            })
        });

        dzClosure.on('complete',function(){
            window.location.href = base_url+'admin/saveProject';
        })
    },
});
Antony
źródło
1

To tylko kolejny przykład tego, jak możesz używać Dropzone.js w istniejącej formie.

dropzone.js:

 init: function() {

   this.on("success", function(file, responseText) {
     //alert("HELLO ?" + responseText); 
     mylittlefix(responseText);
   });

   return noop;
 },

Później w pliku umieściłem

function mylittlefix(responseText) {
  $('#botofform').append('<input type="hidden" name="files[]" value="'+ responseText +'">');
}

Zakłada się, że masz element div o identyfikatorze #botofform , dzięki czemu podczas przesyłania możesz użyć nazw przesłanych plików.

Uwaga: mój skrypt przesyłania zwrócił przesłaną nazwę pliku.jpeg dubbl, uwaga, trzeba również utworzyć skrypt czyszczący, który sprawdzi katalog przesyłania pod kątem nieużywanych plików i usuwa je ... jeśli w nie uwierzytelnionym formularzu interfejsu użytkownika :)

taggart
źródło
Nie powoduje to przesłania obrazów strefy dropzone razem z innymi polami formularza. Zwykle przesyłasz obrazy, zapisujesz nazwy obrazów, a następnie ponownie przesyłasz pozostałe pola formularza z nazwami obrazów.
zen
1

Oto moja próbka oparta na Django + Dropzone. Widok ma zaznaczenie (wymagane) i przesłanie.

<form action="/share/upload/" class="dropzone" id="uploadDropzone">
    {% csrf_token %}
        <select id="warehouse" required>
            <option value="">Select a warehouse</option>
                {% for warehouse in warehouses %}
                    <option value={{forloop.counter0}}>{{warehouse.warehousename}}</option>
                {% endfor %}
        </select>
    <button id="submit-upload btn" type="submit">upload</button>
</form>

<script src="{% static '/js/libs/dropzone/dropzone.js' %}"></script>
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
<script>
    var filename = "";

    Dropzone.options.uploadDropzone = {
        paramName: "file",  // The name that will be used to transfer the file,
        maxFilesize: 250,   // MB
        autoProcessQueue: false,
        accept: function(file, done) {
            console.log(file.name);
            filename = file.name;
            done();    // !Very important
        },
        init: function() {
            var myDropzone = this,
            submitButton = document.querySelector("[type=submit]");

            submitButton.addEventListener('click', function(e) {
                var isValid = document.querySelector('#warehouse').reportValidity();
                e.preventDefault();
                e.stopPropagation();
                if (isValid)
                    myDropzone.processQueue();
            });

            this.on('sendingmultiple', function(data, xhr, formData) {
                formData.append("warehouse", jQuery("#warehouse option:selected").val());
            });
        }
    };
</script>
smartworld-dm
źródło