Интеграция Dropzone.js в существующую HTML-форму с другими полями


181

В настоящее время у меня есть HTML-форма, которую пользователи заполняют детали объявления, которое они хотят опубликовать. Теперь я хочу, чтобы можно было добавить Dropzone для загрузки изображений предмета для продажи.

Я нашел Dropzone.js, который, кажется, делает большую часть того, что мне нужно. Однако при просмотре документации кажется, что вам нужно указать класс всей формы как dropzone(в отличие от только элемента ввода ). Это значит, что вся моя форма становится зоной падения .

Можно ли использовать dropzone только в части моей формы, то есть, только указав элемент как класс "dropzone" , а не всю форму?

Я мог бы использовать отдельные формы, но я хочу, чтобы пользователь мог отправить все это одной кнопкой.

Кроме того, есть ли другая библиотека, которая может сделать это?

Большое спасибо

Ответы:


59

Вот еще один способ сделать это: добавить divв форму форму с зоной отбрасывания классов и программно реализовать ее.

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

Примечание: отключение автообнаружения, иначе Dropzone попытается подключиться дважды

Статья в блоге : Dropzone js + Asp.net: простой способ загрузки групповых изображений


25
При этом он не может использовать кнопку отправки по умолчанию, он не отвечает на его вопрос
clement

5
но это все еще не использует оригинальную форму для отправки
dangel

3
это была моя проблема, и вы решили ее, ty @Satindersingh
Su4p

1
@ Su4p: рад, что это помогает вам, также вы можете проверить ссылку на статью блога для подробного объяснения вместе с опцией изменения размера изображения при загрузке
Satinder singh

2
Это очень помогло, вы можете настроить любой элемент в качестве dropzone, если вы установите URL вручную. Я использовал обработчик успеха, чтобы отправить имя файла в скрытое / отключенное поле в первичной форме.
DigitalDesignDj

40

У меня была точно такая же проблема, и я обнаружил, что ответ Варан Синаи был единственным, который действительно решил исходный вопрос. Этот ответ может быть упрощен, так что вот более простая версия.

Шаги:

  1. Создайте нормальную форму (не забудьте метод и аргументы enctype, так как это больше не обрабатывается dropzone).

  2. Поместите div с помощью class="dropzone"(вот как Dropzone присоединяется к нему) и id="yourDropzoneName"(используется для изменения параметров).

  3. Задайте параметры Dropzone, чтобы задать URL-адрес, где будут публиковаться форма и файлы, деактивировать autoProcessQueue (так происходит только при нажатии пользователем кнопки «Отправить») и разрешить несколько загрузок (если вам это нужно).

  4. Установите функцию init, чтобы использовать Dropzone вместо поведения по умолчанию при нажатии кнопки отправки.

  5. Все еще в функции init используйте обработчик события «sendmultiple» для отправки данных формы вместе с файлами.

Вуаля! Теперь вы можете извлекать данные, как и в обычной форме, в $ _POST и $ _FILES (в примере это происходит в 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());
        });
    }
}

1
Это решение хорошо и работает, но оно больше не перенаправляет на следующую страницу из-за предотвращения поведения отправки по умолчанию.
Феликс Г.

@TIIUNDER - он подготовлен для отправки информации формы через вызов ajax без перезагрузки страницы - вот почему существует e.preventDefault ();
born2fr4g

1
@TIUNDER вы можете добавить редирект в случае успеха
дофламинго

Можно ли отправить форму после processQueue()звонка? Я пытаюсь использовать submit()или click(), оба не работают.
Грей Ли

1
+1 Кажется, это единственное рабочее решение. Вместо того, чтобы делать formData.append один за другим, вы также можете сделать это $(":input[name]", $("form")).each(function () { formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); }); (извините, я не знаю, как поставить переносы здесь)
Aximili

20

«Dropzone.js» - самая распространенная библиотека для загрузки изображений. Если вы хотите, чтобы файл dropzone.js был частью вашей формы, вам следует выполнить следующие шаги:

1) для клиентской части:

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) для серверной части:

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

2
Спасибо за пост! Решил мою проблему. Еще один быстрый вопрос, это не работает, когда нет выбранных изображений (для загрузки), как это решить?
Сато

Кстати: если вы используете действие контроллера с привязкой модели и отправляете свою форму, как эта, модель будет пустой. По какой-то причине этот метод не привязывает фактические данные к модели.
Эдвард Чопурян

1
когда autoProcessQueue = false, никакие события не
запускаются

@Sato при событии нажатия кнопки отправки вы можете проверить длину принятых файлов в dropzone, используя galleryfile.getAcceptedFiles (). Length, и если файл не загружен, вы должны отправить свою форму.
Варан Синаи

@EdwardChopuryan Это не связано с методом подачи данных по зонам. Вероятно, проблема заключается в вашем «соглашении об именовании» входных тегов на вашей платформе, такой как ASP.Net MVC.
Варан Синаи

11

Учебник Enyo отлично.

Я обнаружил, что пример сценария в учебном пособии хорошо работает для кнопки, встроенной в рабочую зону (т. Е. Для элемента формы). Если вы хотите, чтобы кнопка находилась вне элемента формы, я смог выполнить ее, используя событие click:

Во-первых, 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>

Затем тег сценария ....

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

23
Вы не можете иметь форму внутри формы и отправить.
Павел

1
Когда я пытаюсь это сделать, контейнер dropzone-previews, кажется, игнорируется. Dropzone просто добавляет превью к нижней части формы. Вам нужно будет добавить 'previewsContainer:' .dropzone-previews '' в вашу конфигурацию.
Аарон Хилл

6
Это не отвечает на оригинальный вопрос. Первоначальный вопрос заключался в том, как использовать Dropzone с существующей формой, а не о расположении кнопки для запуска действия.
CSSian

7

В дополнение к тому, что говорил sqram, в Dropzone есть дополнительная недокументированная опция «hiddenInputContainer». Все, что вам нужно сделать, это установить эту опцию на селектор формы, к которой вы хотите добавить поле скрытого файла. И вуаля! Поле «.dz-hidden-input», которое Dropzone обычно добавляет к телу, волшебным образом перемещается в вашу форму. Не изменять исходный код Dropzone.

Теперь, пока это работает, чтобы переместить поле файла Dropzone в вашу форму, поле не имеет имени. Так что вам нужно будет добавить:

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

в dropzone.js после этой строки:

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

вокруг линии 547.


5

У меня есть более автоматизированное решение для этого.

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

Нет необходимости отключать DropZone Discovery, и обычная отправка формы сможет отправить файл с любыми другими полями формы через стандартную сериализацию формы.

Этот механизм сохраняет содержимое файла в виде строки base64 в скрытом поле ввода при его обработке. Вы можете декодировать его обратно в бинарную строку в PHP через стандартbase64_decode() метода.

Я не знаю, будет ли этот метод скомпрометирован с большими файлами, но он работает с файлами ~ 40 МБ.


Как вы декодируете и обрабатываете данные из других полей, которые будут представлены вместе с изображениями?
Сэм

@sam Нет необходимости декодировать другие поля. Во-первых, они не кодируются, кодируется только файл.
Умайр Ахмед

Можете ли вы поделиться примером кода для HTML, Javascript и как получить в php laravel.
Сэм

2
Если вы хотите добавить несколько изображений, вы должны удалить входной файл html и добавить его quing js для каждого изображения $ ('# fileUpload'). Append ('<input hidden name = "files []" value =' + content + ' /> ') где контентом является изображение в кодировке base64.
AleXzpm

1
@codepushr - это старый ответ того времени, когда мы не рассматривали платные решения. Теперь мы купили FileUploader, хотя он имеет свои собственные махинации, но достаточно сказать, что он может быть настроен для выполнения практически любых задач.
Умайр Ахмед

4

Вы можете изменить formData, перехватывая событие 'send' из вашей зоны сброса.

dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});

1
Мне нравится этот ответ - но он предполагает, что имя поля и значение были заполнены. Это инициируется при загрузке, которая может происходить в отдельное время перед отправкой формы. Другими словами, вы не можете предполагать, что при отправке изображения форма заполняется.
Энтони

4

Чтобы отправить все файлы вместе с другими данными формы в одном запросе, вы можете скопировать inputв форму временные скрытые узлы Dropzone.js. Вы можете сделать это в addedfilesобработчике событий:

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

Очевидно, что это обходной путь, зависящий от деталей реализации. Связанный исходный код .


Я в основном использовал этот подход, но из-за явных задержек обработки, в конце концов, подключил обработку содержимого файла под myDropzone.on("thumbnail", () => {})событием. Выполнение обработки "addedFile"файла немедленно может быть выполнено undefinedпри доступе.
Матти

Я пытаюсь использовать это, и это помогает привести скрытое поле ввода файла к форме, и когда я отправляю, данные публикации показывают мое поле, files[]но оно не заполнено, независимо от того, что я делаю. Любые идеи? Делать это в Laravel, если это что-то меняет.
Дзен

Здравствуйте! Почему выбранный файл загружается, а если файл удаляется, то нет (ошибка 4)?
Ингус

2

Я хочу внести здесь ответ, поскольку я тоже столкнулся с той же проблемой - мы хотим, чтобы элемент $ _FILES был доступен как часть той же публикации, что и другая форма. Мой ответ основан на @mrtnmgs, однако отмечает комментарии, добавленные к этому вопросу.

Во-первых: Dropzone публикует свои данные через ajax

Тот formData.appendфакт, что вы используете эту опцию, по-прежнему означает, что вы должны выполнять действия UX - то есть все это происходит за кулисами и не является типичной формой публикации. Данные публикуются по вашему urlпараметру.

Во-вторых: если вы хотите имитировать форму сообщения, вам необходимо сохранить опубликованные данные

Для этого требуется код на стороне сервера для сохранения вашего сеанса $_POSTили $_FILESсеанса, который доступен пользователю при загрузке другой страницы, поскольку пользователь не будет переходить на страницу, на которой получены опубликованные данные.

В-третьих: вам нужно перенаправить пользователя на страницу, где действуют эти данные

Теперь вы разместили свои данные, сохранили их в сеансе, вам нужно отобразить / выполнить действие для пользователя на дополнительной странице. Вам также необходимо отправить пользователя на эту страницу.

Итак, для моего примера:

[Код Dropzone: использует 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';
        })
    },
});

1

Это просто еще один пример того, как вы можете использовать Dropzone.js в существующей форме.

dropzone.js:

 init: function() {

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

   return noop;
 },

Затем, позже в файл я положил

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

Это предполагает, что у вас есть div с id #botofformтаким образом, что при загрузке вы можете использовать имена загруженных файлов.

Примечание: мой скрипт загрузки возвратил дубликаты theuploadedfilename.jpeg, вам также потребуется создать сценарий очистки, который проверяет каталог загрузки на наличие файлов, которые не используются, и удаляет их ..if в внешнем интерфейсе без аутентификации :)


Это не передает изображения dropzone вместе с другими полями формы. Что вы делаете, так это загружаете изображения обычным образом, сохраняете имена изображений, а затем повторно отправляете остальные поля формы с именами изображений.
Дзен

1

Вот мой пример, основанный на Django + Dropzone. Просмотр имеет выбрать (обязательно) и отправить.

<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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.