Integrando Dropzone.js en el formulario HTML existente con otros campos

181

Actualmente tengo un formulario HTML en el que los usuarios completan los detalles de un anuncio que desean publicar. Ahora quiero poder agregar una zona de caída para subir imágenes del artículo a la venta.

He encontrado Dropzone.js que parece hacer la mayor parte de lo que necesito. Sin embargo, al examinar la documentación, parece que debe especificar la clase de todo el formulario como dropzone(en lugar de solo el elemento de entrada ). Esto significa que toda mi forma se convierte en la zona de caída .

¿Es posible utilizar la zona de caída en solo una parte de mi formulario, es decir, especificando solo el elemento como clase "zona de caída" , en lugar de todo el formulario?

Podría usar formularios separados, pero quiero que el usuario pueda enviarlo todo con un solo botón.

Alternativamente, ¿hay otra biblioteca que pueda hacer esto?

Muchas gracias

Ben Thompson
fuente

Respuestas:

59

Aquí hay otra forma de hacerlo: agregue un diven su formulario con un nombre de clase dropzone e implemente dropzone mediante programación.

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

Nota: Deshabilitar detección automática, de lo contrario, Dropzone intentará adjuntar dos veces

Artículo de blog : Dropzone js + Asp.net: forma fácil de cargar imágenes a granel

Satinder singh
fuente
25
Con eso, no puede usar el botón de envío predeterminado, no responde a su pregunta
clement
55
pero esto todavía no usa el formulario original para enviar
peligro
3
este fue mi problema y lo resolvió, escriba @Satindersingh
Su4p
1
@ Su4p: me alegra que te ayude, también puedes consultar el enlace del artículo del blog para obtener una explicación detallada junto con la opción de cambiar el tamaño de la imagen durante la carga
Satinder singh
2
Esto ayudó mucho, puede configurar cualquier elemento como una zona de caída si configura la URL manualmente. Utilicé el controlador de éxito para publicar el nombre de archivo en un campo oculto / deshabilitado en el formulario principal.
DigitalDesignDj
40

Tuve exactamente el mismo problema y descubrí que la respuesta de Varan Sinayee fue la única que realmente resolvió la pregunta original. Sin embargo, esa respuesta se puede simplificar, así que aquí hay una versión más simple.

Los pasos son:

  1. Crea una forma normal (no olvides el método y los argumentos enctype ya que dropzone ya no lo maneja).

  2. Pon un div dentro con class="dropzone"(así es como Dropzone se une a él) y id="yourDropzoneName"(usado para cambiar las opciones).

  3. Establezca las opciones de Dropzone, para establecer la url donde se publicarán el formulario y los archivos, desactive autoProcessQueue (por lo que solo ocurre cuando el usuario presiona 'enviar') y permite múltiples cargas (si lo necesita).

  4. Configure la función init para usar Dropzone en lugar del comportamiento predeterminado cuando se hace clic en el botón Enviar.

  5. Aún en la función init, use el controlador de eventos "sendmultiple" para enviar los datos del formulario junto con los archivos.

Voilà! Ahora puede recuperar los datos como lo haría con un formulario normal, en $ _POST y $ _FILES (en el ejemplo, esto sucedería en 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
fuente
1
Esta solución es agradable y funciona, pero ya no redirige a la página siguiente, debido a que evita el comportamiento de envío predeterminado.
Felix G.
@TIIUNDER: está preparado para enviar información del formulario a través de una llamada ajax sin recargar la página, es por eso que hay e.preventDefault ();
born2fr4g
1
@TIUNDER puede agregar redireccionamiento en caso de éxito
doflamingo
¿Es posible enviar el formulario después de la processQueue()llamada? Intento usar submit()o click(), ambos no funcionan.
Gray Li
1
+1 Esta parece ser la única solución de trabajo. En lugar de hacer formData.append uno por uno, también puede hacerlo $(":input[name]", $("form")).each(function () { formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); }); (lo siento, no sé cómo poner saltos de línea aquí)
Aximili
20

El "dropzone.js" es la biblioteca más común para cargar imágenes. Si desea tener "dropzone.js" como parte de su formulario, debe seguir los siguientes pasos:

1) para el lado del cliente:

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) para el lado del servidor:

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
fuente
2
¡Gracias por la publicacion! Resuelto mi problema Otra pregunta rápida, esto no funciona cuando no hay imágenes seleccionadas (para cargar), ¿cómo resolver esto?
Sato
Por cierto: si usa la acción del controlador con el enlace del modelo y envía su formulario de esta manera, el modelo estará vacío. Por alguna razón, este método no vincula los datos reales al modelo.
Edward Chopuryan
1
cuando autoProcessQueue = eventos falsos no se disparan
Cyril
@Sato al hacer clic en el evento del botón de envío, puede verificar la longitud de los archivos aceptados en dropzone usando galleryfile.getAcceptedFiles (). Length y si no hay ningún archivo cargado, debe enviar su formulario.
Varan Sinayee
@EdwardChopuryan Esto no está relacionado con el método de envío de datos por dropzone. Probablemente el problema esté en su "convención de nomenclatura" de etiquetas de entrada en su plataforma, como ASP.Net MVC.
Varan Sinayee
11

El tutorial de Enyo es excelente.

Descubrí que el script de muestra en el tutorial funcionó bien para un botón incrustado en la zona de caída (es decir, el elemento de formulario). Si desea tener el botón fuera del elemento de formulario, pude lograrlo usando un evento de clic:

Primero, el 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>

Entonces, la etiqueta del 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
fuente
23
No puede tener un formulario dentro de un formulario y enviarlo.
Paul
1
Cuando intento esto, el contenedor dropzone-previews parece ignorarse. Dropzone solo agrega las vistas previas al final del formulario. Deberá agregar 'previewsContainer:' .dropzone-previews ',' a su configuración.
Aaron Hill
66
Esto no responde a la pregunta original. La pregunta original era cómo usar Dropzone con un formulario existente, no sobre la ubicación del botón para activar la acción.
CSSian
7

Además de lo que estaba diciendo sqram, Dropzone tiene una opción adicional no documentada, "hiddenInputContainer". Todo lo que tiene que hacer es establecer esta opción en el selector del formulario al que desea que se agregue el campo del archivo oculto. ¡Y voilá! El campo de archivo ".dz-hidden-input" que Dropzone normalmente agrega al cuerpo se mueve mágicamente en su forma. Sin alterar el código fuente de Dropzone.

Ahora, mientras esto funciona para mover el campo del archivo Dropzone a su formulario, el campo no tiene nombre. Por lo tanto, deberá agregar:

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

a dropzone.js después de esta línea:

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

alrededor de la línea 547.

Dragón codificado
fuente
5

Tengo una solución más automatizada para esto.

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

No es necesario deshabilitar DropZone Discovery y el envío de formulario normal podrá enviar el archivo con cualquier otro campo de formulario a través de la serialización de formulario estándar.

Este mecanismo almacena el contenido del archivo como una cadena base64 en el campo de entrada oculto cuando se procesa. Puede decodificarlo de nuevo a una cadena binaria en PHP a través del base64_decode()método estándar .

No sé si este método se verá comprometido con archivos grandes, pero funciona con archivos de ~ 40 MB.

Umair Ahmed
fuente
¿Cómo decodifica y procesa los datos de otros campos que se enviarán junto con las imágenes?
Sam
@sam No hay necesidad de decodificar los otros campos. En primer lugar, no se codifican, solo se codifica el archivo.
Umair Ahmed
¿Puedes compartir algún código de muestra para html, javascript y cómo recuperarlo en laravel php?
sam
2
Si desea agregar varias imágenes, debe eliminar la entrada del archivo html y agregarla con js para cada imagen $ ('# fileUpload'). Append ('<input hidden name = "files []" value =' + content + ' /> ') donde el contenido es la imagen codificada en base64.
AleXzpm
1
@codepushr bueno, es una vieja respuesta del tiempo que no estábamos considerando soluciones pagas. Ahora hemos comprado FileUploader, aunque tiene sus propias travesuras, pero basta con decir que se puede personalizar para hacer casi cualquier cosa.
Umair Ahmed
4

Puede modificar el formData capturando el evento 'envío' desde su zona de caída.

dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});
shawnrushefsky
fuente
1
Me gusta esta respuesta, pero supondría que el nombre de campo y el valor se han rellenado. Esto se activa en la carga que puede ocurrir en un momento separado al envío del formulario. En otras palabras, no puede suponer que al enviar la imagen se completa el formulario.
Antony
4

Para enviar todos los archivos junto con otros datos de formulario en una sola solicitud, puede copiar inputnodos ocultos temporales de Dropzone.js en su formulario. Puede hacerlo dentro del addedfilescontrolador de eventos:

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

Obviamente, esta es una solución alternativa que depende de los detalles de implementación. Código fuente relacionado .

Leonid Vasilev
fuente
Básicamente utilicé este enfoque, pero debido a los aparentes retrasos en el procesamiento, al final conecté el procesamiento del contenido del archivo en el myDropzone.on("thumbnail", () => {})evento. Hacer el procesamiento inmediatamente en "addedFile"el archivo aún podría realizarse undefinedal acceder.
Matti
Estoy tratando de usar esto y funciona para traer el campo de entrada del archivo oculto al formulario, y cuando envío, los datos de la publicación muestran mi campo files[]pero está en blanco, no importa lo que haga. ¿Algunas ideas? Haciéndolo en Laravel si hace alguna diferencia.
zen
¡Hola! ¿Por qué el archivo seleccionado se carga pero si el archivo se cae, entonces no (error 4)?
Ingus
2

Quiero contribuir con una respuesta aquí, ya que yo también me he enfrentado al mismo problema: queremos que el elemento $ _FILES esté disponible como parte de la misma publicación que otro formulario. Mi respuesta se basa en @mrtnmgs, sin embargo, toma nota de los comentarios agregados a esa pregunta.

En primer lugar: Dropzone publica sus datos a través de ajax

El hecho de que use la formData.appendopción significa que debe abordar las acciones de UX, es decir, todo esto ocurre detrás de escena y no es una publicación de formulario típica. Los datos se publican en su urlparámetro.

En segundo lugar: si desea imitar una publicación de formulario, deberá almacenar los datos publicados

Esto requiere un código del lado del servidor para almacenar su $_POSTo $_FILESen una sesión que esté disponible para el usuario en otra carga de página, ya que el usuario no irá a la página donde se reciben los datos publicados.

En tercer lugar: debe redirigir al usuario a la página donde se activan estos datos.

Ahora que ha publicado sus datos, los ha almacenado en una sesión, debe mostrarlos / activarlos para el usuario en una página adicional. También debe enviar al usuario a esa página.

Entonces para mi ejemplo:

[Código de Dropzone: utiliza 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';
        })
    },
});
Antonio
fuente
1

Este es solo otro ejemplo de cómo puede usar Dropzone.js en un formulario existente.

dropzone.js:

 init: function() {

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

   return noop;
 },

Luego, más adelante en el archivo puse

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

Esto supone que tiene un div con id de #botofformesa manera cuando carga puede usar los nombres de los archivos cargados.

Nota: mi secuencia de comandos de carga devolvió la dubblenote de nombre de archivo cargado. Jpeg. También necesitaría crear una secuencia de comandos de limpieza que verifique el directorio de carga en busca de archivos no utilizados y los elimine ...

Taggart
fuente
Esto no envía imágenes de zonas de caída junto con otros campos de formulario. Lo que está haciendo es cargar imágenes normalmente, guardar los nombres de las imágenes y luego volver a enviar el resto de los campos del formulario con los nombres de las imágenes.
zen
1

Aquí está mi muestra, se basa en Django + Dropzone. Ver tiene seleccionar (requerido) y enviar.

<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
fuente