Intégration de Dropzone.js dans un formulaire HTML existant avec d'autres champs

181

J'ai actuellement un formulaire HTML dans lequel les utilisateurs remplissent les détails d'une annonce qu'ils souhaitent publier. Je souhaite maintenant pouvoir ajouter une zone de dépôt pour télécharger des images de l'article à vendre.

J'ai trouvé Dropzone.js qui semble faire la plupart de ce dont j'ai besoin. Cependant, lorsque vous consultez la documentation, il semble que vous deviez spécifier la classe du formulaire entier comme dropzone(par opposition à l' élément d' entrée uniquement ). Cela signifie alors que tout mon formulaire devient la zone de dépôt .

Est-il possible d'utiliser la zone de dépôt uniquement dans une partie de mon formulaire, c'est-à-dire en spécifiant uniquement l'élément en tant que classe "dropzone" , plutôt que la forme entière?

Je pourrais utiliser des formulaires séparés, mais je veux que l'utilisateur puisse tout soumettre avec un seul bouton.

Sinon, existe-t-il une autre bibliothèque qui peut faire cela?

Merci beaucoup

Ben Thompson
la source

Réponses:

59

Voici une autre façon de le faire: ajoutez un divdans votre formulaire avec un nom de classe dropzone et implémentez dropzone par programme.

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

Remarque: désactivation de la découverte automatique, sinon Dropzone essaiera de se connecter deux fois

Article de blog : Dropzone js + Asp.net: un moyen facile de télécharger des images en masse

Satinder singh
la source
25
Avec ça, il ne peut pas utiliser le bouton d'envoi par défaut, il ne répond pas à sa question
clément
5
mais cela n'utilise toujours pas le formulaire original pour soumettre
dangel
3
c'était mon problème et vous l'avez résolu, ty @Satindersingh
Su4p
1
@ Su4p: je suis heureux que cela vous aide, vous pouvez également consulter le lien de l'article du blog pour plus de détails, ainsi que l'option de redimensionnement de l'image lors du téléchargement
Satinder singh
2
Cela a beaucoup aidé, vous pouvez configurer n'importe quel élément en tant que zone de dépôt si vous configurez l'url manuellement. J'ai utilisé le gestionnaire de succès pour publier le nom de fichier dans un champ masqué / désactivé dans le formulaire principal.
DigitalDesignDj
40

J'ai eu exactement le même problème et j'ai trouvé que la réponse de Varan Sinayee était la seule qui résolvait réellement la question initiale. Cette réponse peut être simplifiée, alors voici une version plus simple.

Les étapes sont:

  1. Créez une forme normale (n'oubliez pas la méthode et l'enctype args car cela n'est plus géré par dropzone).

  2. Mettez un div à l'intérieur avec le class="dropzone"(c'est ainsi que Dropzone s'y attache) et id="yourDropzoneName"(utilisé pour changer les options).

  3. Définissez les options de Dropzone, pour définir l'url où le formulaire et les fichiers seront publiés, désactivez autoProcessQueue (cela ne se produit que lorsque l'utilisateur appuie sur `` soumettre '') et autorisez plusieurs téléchargements (si vous en avez besoin).

  4. Définissez la fonction init pour utiliser Dropzone au lieu du comportement par défaut lorsque le bouton d'envoi est cliqué.

  5. Toujours dans la fonction init, utilisez le gestionnaire d'événements "sendmultiple" pour envoyer les données du formulaire avec les fichiers.

Voilà! Vous pouvez maintenant récupérer les données comme vous le feriez avec un formulaire normal, dans $ _POST et $ _FILES (dans l'exemple cela se produirait dans 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
la source
1
Cette solution est agréable et fonctionne, mais elle ne redirige plus vers la page suivante, car elle empêche le comportement de soumission par défaut.
Felix G.
@TIIUNDER - il est préparé pour envoyer des informations de formulaire via un appel ajax sans rechargement de page - c'est pourquoi il y a e.preventDefault ();
born2fr4g
1
@TIUNDER, vous pouvez ajouter une redirection dans l'événement de réussite
doflamingo
Est-il possible de soumettre le formulaire après l' processQueue()appel? J'essaie d'utiliser submit()ou click(), les deux ne fonctionnent pas.
Gray Li
1
+1 Cela semble être la seule solution qui fonctionne. Au lieu de faire formData.append un par un, vous pouvez également le faire $(":input[name]", $("form")).each(function () { formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); }); (désolé je ne sais pas comment mettre des sauts de ligne ici)
Aximili
20

"Dropzone.js" est la bibliothèque la plus courante pour télécharger des images. Si vous souhaitez que "dropzone.js" ne fasse partie de votre formulaire, vous devez suivre les étapes suivantes:

1) pour le côté client:

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) pour le côté serveur:

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
la source
2
Merci pour le post! J'ai résolu mon problème. Autre question rapide, cela ne fonctionne pas lorsqu'aucune image n'est sélectionnée (à télécharger), comment résoudre ce problème?
Sam
BTW: si vous utilisez l'action du contrôleur avec la liaison de modèle et soumettez votre formulaire comme ceci, le modèle sera vide. Pour une raison quelconque, cette méthode ne lie pas les données réelles au modèle.
Edward Chopuryan
1
quand autoProcessQueue = false aucun événement n'est déclenché
cyril
@Sato on click événement du bouton de soumission, vous pouvez vérifier la longueur des fichiers acceptés sur dropzone en utilisant galleryfile.getAcceptedFiles (). Length et s'il n'y a pas de fichier téléchargé, vous devez soumettre votre formulaire.
Varan Sinayee
@EdwardChopuryan Ceci n'est pas lié à la méthode de soumission des données par dropzone. Le problème vient probablement de votre "convention de dénomination" des balises d'entrée sur votre plate-forme comme ASP.Net MVC.
Varan Sinayee
11

Le tutoriel d'Enyo est excellent.

J'ai trouvé que l'exemple de script dans le didacticiel fonctionnait bien pour un bouton intégré dans la zone de dépôt (c'est-à-dire l'élément de formulaire). Si vous souhaitez avoir le bouton en dehors de l'élément de formulaire, j'ai pu le faire en utilisant un événement click:

Tout d'abord, le 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>

Ensuite, la balise 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
la source
23
Vous ne pouvez pas avoir un formulaire dans un formulaire et le soumettre.
paul
1
Quand j'essaye ceci, le conteneur dropzone-previews semble être ignoré. Dropzone ajoute simplement les aperçus au bas du formulaire. Vous devrez ajouter 'previewsContainer:' .dropzone-previews ',' à votre configuration.
Aaron Hill
6
Cela ne répond pas à la question initiale. La question initiale était de savoir comment utiliser Dropzone avec un formulaire existant, et non sur l'emplacement du bouton pour déclencher l'action.
CSSian
7

En plus de ce que disait sqram, Dropzone a une option supplémentaire non documentée, "hiddenInputContainer". Tout ce que vous avez à faire est de définir cette option sur le sélecteur du formulaire auquel vous souhaitez ajouter le champ de fichier caché. Et voila! Le champ de fichier ".dz-hidden-input" que Dropzone ajoute normalement au corps se déplace comme par magie dans votre formulaire. Aucune modification du code source de Dropzone.

Maintenant que cela fonctionne pour déplacer le champ de fichier Dropzone dans votre formulaire, le champ n'a pas de nom. Vous devrez donc ajouter:

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

à dropzone.js après cette ligne:

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

vers la ligne 547.

Codedragon
la source
5

J'ai une solution plus automatisée pour cela.

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

Pas besoin de désactiver DropZone Discovery et la soumission de formulaire normale sera en mesure d'envoyer le fichier avec tous les autres champs de formulaire via la sérialisation de formulaire standard.

Ce mécanisme stocke le contenu du fichier sous forme de chaîne base64 dans le champ de saisie masqué lorsqu'il est traité. Vous pouvez le décoder en chaîne binaire en PHP via la base64_decode()méthode standard .

Je ne sais pas si cette méthode sera compromise avec des fichiers volumineux, mais elle fonctionne avec des fichiers ~ 40 Mo.

Umair Ahmed
la source
Comment décodez-vous et traitez-vous les données d'autres champs qui seront soumis avec les images?
sam
@sam Il n'est pas nécessaire de décoder les autres champs. Ils ne sont pas encodés en premier lieu, seul le fichier est encodé.
Umair Ahmed
Pouvez-vous partager un exemple de code pour le html, javascript et comment récupérer dans laravel php.
sam
2
Si vous souhaitez ajouter plusieurs images, vous devez supprimer l'entrée de fichier html et l'ajouter quing js pour chaque image $ ('# fileUpload'). Append ('<input hidden name = "files []" value =' + content + ' /> ') où le contenu est l'image encodée en base64.
AleXzpm
1
@codepushr et bien c'est une vieille réponse de l'époque où nous n'envisagions pas de solutions payantes. Maintenant, nous avons acheté FileUploader, bien qu'il ait ses propres manigances, mais il suffit de dire qu'il peut être personnalisé pour faire presque n'importe quoi.
Umair Ahmed le
4

Vous pouvez modifier le formData en capturant l'événement 'envoi' depuis votre zone de dépôt.

dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});
shawnrushefsky
la source
1
J'aime cette réponse - mais cela suppose que le nom de champ et la valeur ont été renseignés. Ceci est déclenché lors du téléchargement qui peut se produire à un moment différent de la soumission du formulaire. En d'autres termes, vous ne pouvez pas supposer que lors de l'envoi de l'image, le formulaire est rempli.
Antony le
4

Afin de soumettre tous les fichiers avec d'autres données de formulaire en une seule demande, vous pouvez copier les inputnœuds cachés temporaires de Dropzone.js dans votre formulaire. Vous pouvez le faire dans le addedfilesgestionnaire d'événements:

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

Il s'agit évidemment d'une solution de contournement dépendante des détails de mise en œuvre. Code source associé .

Léonid Vasilev
la source
J'ai essentiellement utilisé cette approche, mais en raison de retards de traitement apparents, j'ai finalement raccordé le traitement du contenu du fichier sous l' myDropzone.on("thumbnail", () => {})événement. Le traitement immédiat "addedFile"du fichier peut toujours être effectué undefinedlors de l'accès.
Matti
J'essaye d'utiliser ceci et cela fonctionne en apportant le champ d'entrée de fichier caché au formulaire, et quand je soumets, les données de publication montrent mon champ files[]mais il est vide quoi que je fasse. Des idées? Le faire à Laravel si cela fait une différence.
zen
salut! Pourquoi le fichier sélectionné est-il téléchargé mais si le fichier est supprimé, alors non (erreur 4)?
Ingus
2

Je veux apporter une réponse ici car j'ai moi aussi été confronté au même problème - nous voulons que l'élément $ _FILES soit disponible dans le même message qu'un autre formulaire. Ma réponse est basée sur @mrtnmgs mais note les commentaires ajoutés à cette question.

Premièrement: Dropzone publie ses données via ajax

Juste parce que vous utilisez l' formData.appendoption signifie toujours que vous devez vous attaquer aux actions UX - c'est-à-dire que tout se passe dans les coulisses et n'est pas un post de formulaire typique. Les données sont enregistrées dans votre urlparamètre.

Deuxièmement: si vous souhaitez par conséquent imiter une publication de formulaire, vous devrez stocker les données publiées

Cela nécessite un code côté serveur pour stocker votre $_POSTou $_FILESdans une session qui est disponible pour l'utilisateur sur un autre chargement de page car l'utilisateur n'ira pas à la page où les données publiées sont reçues.

Troisièmement: vous devez rediriger l'utilisateur vers la page sur laquelle ces données sont exploitées

Maintenant que vous avez publié vos données, les avez stockées dans une session, vous devez les afficher / les actionner pour l'utilisateur dans une page supplémentaire. Vous devez également envoyer l'utilisateur sur cette page.

Donc pour mon exemple:

[Code Dropzone: utilise 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
la source
1

Ceci est juste un autre exemple de la façon dont vous pouvez utiliser Dropzone.js dans un formulaire existant.

dropzone.js:

 init: function() {

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

   return noop;
 },

Puis, plus tard dans le fichier j'ai mis

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

Cela suppose que vous ayez un div avec un identifiant de #botofformcette façon lors du téléchargement, vous pouvez utiliser les noms des fichiers téléchargés.

Remarque: mon script de téléchargement a renvoyé le fichier uploadedfilename.jpeg dubblenote, vous devrez également créer un script de nettoyage qui vérifie le répertoire de téléchargement pour les fichiers non utilisés et les supprime .. si dans un formulaire non authentifié frontal :)

taggart
la source
Cela ne soumet pas les images dropzone avec d'autres champs de formulaire. Ce que vous faites est de télécharger des images normalement, d'enregistrer les noms d'image, puis de soumettre à nouveau le reste des champs de formulaire avec les noms d'image.
zen
1

Voici mon exemple, basé sur Django + Dropzone. La vue a sélectionné (obligatoire) et soumettre.

<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
la source