Integrando o Dropzone.js ao formulário HTML existente com outros campos

181

Atualmente, tenho um formulário HTML em que os usuários preenchem os detalhes de um anúncio que desejam publicar. Agora, desejo poder adicionar um dropzone para fazer upload de imagens do item à venda.

Eu encontrei o Dropzone.js que parece fazer a maior parte do que eu preciso. No entanto, ao examinar a documentação, parece que você precisa especificar a classe de todo o formulário como dropzone(em vez de apenas o elemento de entrada ). Isso significa que todo o meu formulário se torna o dropzone .

É possível usar o dropzone em apenas parte do meu formulário, ou seja, especificando apenas o elemento como classe "dropzone" , em vez de todo o formulário?

Eu poderia usar formulários separados, mas quero que o usuário possa enviar tudo com um botão.

Como alternativa, existe outra biblioteca que pode fazer isso?

Muito Obrigado

Ben Thompson
fonte

Respostas:

59

Aqui está outra maneira de fazer isso: adicione um divno seu formulário com um nome de classe dropzone e implemente o dropzone programaticamente.

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: Desativando a descoberta automática, caso contrário, o Dropzone tentará anexar duas vezes

Artigo do blog : Dropzone js + Asp.net: Maneira fácil de fazer upload de imagens em massa

Satinder singh
fonte
25
Com isso, ele não pode usar o botão de envio padrão, ele não responde à sua pergunta
Clement
5
mas isso ainda não usa o formulário original para enviar
dangel 30/05
3
este foi o meu problema e você o resolveu, ty @Satindersingh
Su4p 14/15
1
@ Su4p: estou contente ele ajuda você, você também pode conferir link do artigo blog para detalhes explicação juntamente com opção de imagem redimensionamento enquanto upload
Satinder singh
2
Isso ajudou bastante, você pode configurar qualquer elemento como uma zona de drop se configurar o URL manualmente. Usei o manipulador de sucesso para postar o nome do arquivo em um campo oculto / desativado no formulário principal.
DigitalDesignDj
40

Eu tive exatamente o mesmo problema e descobri que a resposta de Varan Sinayee foi a única que realmente resolveu a pergunta original. Essa resposta pode ser simplificada, então aqui está uma versão mais simples.

Os passos são:

  1. Crie um formulário normal (não esqueça o método e enctype args, pois isso não é mais tratado pelo dropzone).

  2. Coloque uma div dentro com o class="dropzone"(é assim que o Dropzone se liga a ele) e id="yourDropzoneName"(usado para alterar as opções).

  3. Defina as opções do Dropzone, para definir o URL onde o formulário e os arquivos serão publicados, desative o autoProcessQueue (para que isso aconteça apenas quando o usuário pressionar 'enviar') e permita vários uploads (se necessário).

  4. Defina a função init para usar o Dropzone em vez do comportamento padrão quando o botão Enviar for clicado.

  5. Ainda na função init, use o manipulador de eventos "sendmultiple" para enviar os dados do formulário junto com os arquivos.

Voilà! Agora você pode recuperar os dados como faria com um formulário normal, em $ _POST e $ _FILES (no exemplo, isso aconteceria em 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
fonte
1
Essa solução é boa e funciona, mas não é mais redirecionada para a próxima página, por impedir o comportamento de envio padrão.
Felix G.
@TIIUNDER - está preparado para enviar informações de formulários via chamada ajax sem recarregar a página - é por isso que existe e.preventDefault ();
born2fr4g
1
@TIUNDER você pode adicionar redirecionamento em caso de sucesso
Doflamingo
É possível enviar o formulário após a processQueue()chamada? Eu tento usar submit()ou click(), ambos não funcionam.
Gray Li
1
+1 Esta parece ser a única solução funcional. Em vez de fazer formData.append um por um, você também pode fazer $(":input[name]", $("form")).each(function () { formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); }); (desculpe, eu não sei como colocar quebras de linha aqui)
Aximili
20

O "dropzone.js" é a biblioteca mais comum para o upload de imagens. Se você deseja que o "dropzone.js" seja apenas parte do seu formulário, execute as seguintes etapas:

1) para o lado do 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 o lado do 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
fonte
2
Obrigado pelo post! Resolveu o meu problema. Outra pergunta rápida, isso não funciona quando nenhuma imagem selecionada (para carregar), como resolver isso?
Sato
BTW: se você usar a ação do controlador com ligação do modelo e enviar seu formulário como este, o modelo estará vazio. Por alguma razão, esse método não vincula os dados reais ao modelo.
Edward Chopuryan
1
quando autoProcessQueue = eventos sem falsos são disparados
Cyril
No evento @Sato on click do botão enviar, você pode verificar o tamanho dos arquivos aceitos no dropzone usando galleryfile.getAcceptedFiles (). Length e, se não houver nenhum arquivo carregado, envie seu formulário.
Varan Sinayee 20/09/18
@EdwardChopuryan Isso não está relacionado ao método de envio de dados pelo dropzone. Provavelmente, o problema está na sua "convenção de nomenclatura" de tags de entrada na sua plataforma, como o ASP.Net MVC.
Varan Sinayee 20/09/18
11

O tutorial do Enyo é excelente.

Descobri que o script de exemplo no tutorial funcionava bem para um botão incorporado no dropzone (ou seja, o elemento do formulário). Se você deseja ter o botão fora do elemento do formulário, consegui fazê-lo usando um evento click:

Primeiro, o 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>

Em seguida, a tag de 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
fonte
23
Você não pode ter um formulário dentro de um formulário e enviar.
paul
1
Quando eu tento isso, o container dropzone-previews parece ser ignorado. O Dropzone apenas adiciona as visualizações na parte inferior do formulário. Você precisará adicionar 'previewsContainer:' .dropzone-previews ',' à sua configuração.
Aaron Hill
6
Isso não responde à pergunta original. A pergunta original era como usar o Dropzone com um formulário existente, não sobre a localização do botão para acionar a ação.
CSSian
7

Além do que o sqram estava dizendo, o Dropzone possui uma opção adicional não documentada, "hiddenInputContainer". Tudo o que você precisa fazer é definir essa opção para o seletor do formulário ao qual você deseja que o campo do arquivo oculto seja anexado. E pronto! O campo do arquivo ".dz-hidden-input" que o Dropzone normalmente adiciona ao corpo se move magicamente para o seu formulário. Não é possível alterar o código-fonte do Dropzone.

Agora, enquanto isso funciona para mover o campo do arquivo Dropzone para o seu formulário, o campo não tem nome. Então você precisará adicionar:

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

para dropzone.js após esta linha:

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

em torno da linha 547.

Codedragon
fonte
5

Eu tenho uma solução mais automatizada para isso.

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

Não há necessidade de desativar o DropZone Discovery e o envio normal do formulário poderá enviar o arquivo com outros campos através da serialização padrão.

Esse mecanismo armazena o conteúdo do arquivo como string base64 no campo de entrada oculto quando é processado. Você pode decodificá-lo de volta à string binária no PHP através do base64_decode()método padrão .

Não sei se esse método será comprometido com arquivos grandes, mas funciona com ~ 40 MB.

Umair Ahmed
fonte
Como você decodifica e processa dados de outros campos que serão enviados junto com as imagens?
sam
@ sam Não há necessidade de decodificar os outros campos. Eles não estão sendo codificados em primeiro lugar, apenas o arquivo é codificado.
Umair Ahmed
Você pode compartilhar algum código de exemplo para html, javascript e como recuperar em laravel php.
sam
1
Se você deseja adicionar várias imagens, remova a entrada do arquivo html e adicione-a quing js para cada imagem $ ('# fileUpload'). Append ('<input hidden name = "files []" value =' + content + ' /> ') onde o conteúdo é a imagem codificada em base64.
AleXzpm
1
@codepushr bem, é uma resposta antiga da época em que não estávamos considerando soluções pagas. Agora nós compramos o FileUploader, embora ele tenha suas próprias travessuras, mas basta dizer que pode ser personalizado para fazer quase qualquer coisa.
Umair Ahmed 15/01
4

Você pode modificar os dados do formulário capturando o evento 'send' no seu dropzone.

dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});
shawnrushefsky
fonte
1
Eu gosto desta resposta - mas assumiria que o nome e o valor do campo foram preenchidos. Isso é acionado no upload, que pode ocorrer em um momento separado do envio do formulário. Em outras palavras, você não pode assumir que, ao enviar a imagem, o formulário é preenchido.
Antony
4

Para enviar todos os arquivos juntamente com outros dados do formulário em uma única solicitação, você pode copiar os inputnós ocultos temporários do Dropzone.js no seu formulário. Você pode fazer isso no addedfilesmanipulador 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, essa é uma solução alternativa dependente dos detalhes da implementação. Código fonte relacionado .

Leonid Vasilev
fonte
Basicamente, usei essa abordagem, mas devido a aparentes atrasos no processamento, no final, liguei o processamento do conteúdo do arquivo no myDropzone.on("thumbnail", () => {})evento. A execução do processamento imediatamente no "addedFile"arquivo ainda pode estar undefinedapós o acesso.
Matti
Estou tentando usar isso e ele funciona para trazer o campo de entrada de arquivo oculto para o formulário e, quando envio, os dados da postagem mostram meu campo, files[]mas ficam em branco, não importa o que eu faça. Alguma ideia? Fazê-lo no Laravel, se faz alguma diferença.
zen
Olá! Por que o arquivo selecionado é carregado, mas se o arquivo é descartado, não é (erro 4)?
Ingus 11/06
2

Quero contribuir com uma resposta aqui, pois também enfrentei o mesmo problema - queremos que o elemento $ _FILES esteja disponível como parte da mesma postagem em outro formulário. Minha resposta é baseada em @mrtnmgs, no entanto, observa os comentários adicionados a essa pergunta.

Em primeiro lugar: o Dropzone publica seus dados via ajax

Só porque você usa a formData.appendopção ainda significa que você deve enfrentar as ações de UX - isto é, tudo acontece nos bastidores e não é um post de formulário típico. Os dados são lançados no seu urlparâmetro.

Segundo: se você deseja imitar uma postagem de formulário, será necessário armazenar os dados postados

Isso requer que o código do servidor armazene sua $_POSTou $_FILESem uma sessão disponível para o usuário em outro carregamento de página, pois o usuário não irá para a página em que os dados publicados são recebidos.

Terceiro: você precisa redirecionar o usuário para a página em que esses dados são acionados

Agora que você publicou seus dados, os armazenou em uma sessão, é necessário exibi-los / ação para o usuário em uma página adicional. Você precisa enviar o usuário para essa página também.

Então, para o meu exemplo:

[Código do Dropzone: usa o 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
fonte
1

Este é apenas outro exemplo de como você pode usar o Dropzone.js em um formulário existente.

dropzone.js:

 init: function() {

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

   return noop;
 },

Então, mais tarde no arquivo eu coloquei

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

Isso pressupõe que você tenha uma div com o ID #botofformdessa maneira. Ao fazer o upload, você pode usar os nomes dos arquivos enviados.

Nota: meu script de upload retornou oenuploadedfilename.jpeg dubblenote, você também precisaria criar um script de limpeza que verifique o diretório de upload em busca de arquivos não utilizados e os exclua ..se em um formulário não autenticado de front-end :)

taggart
fonte
Isso não envia imagens da zona de lançamento juntamente com outros campos do formulário. O que você está fazendo é enviar imagens normalmente, salvar os nomes das imagens e reenviar o restante dos campos do formulário com os nomes das imagens.
zen
1

Aqui está o meu exemplo, é baseado no Django + Dropzone. A exibição selecionou (obrigatório) e enviou.

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