将 Dropzone.js 与其他字段集成到现有的 HTML 表单中

Posted

技术标签:

【中文标题】将 Dropzone.js 与其他字段集成到现有的 HTML 表单中【英文标题】:Integrating Dropzone.js into existing HTML form with other fields 【发布时间】:2013-07-26 04:42:15 【问题描述】:

我目前有一个 html 表单,用户可以在其中填写他们希望发布的广告的详细信息。我现在希望能够添加一个 dropzone 来上传待售商品的图片。

我找到了Dropzone.js,它似乎可以满足我的大部分需求。但是,在查看文档时,您似乎需要将整个表单的类指定为 dropzone(而不仅仅是 input 元素)。这意味着我的整个表单变成了 dropzone

是否可以在我的表单的一部分中使用 dropzone,即仅通过将元素指定为类 "dropzone",而不是整个表单?

我可以使用单独的表单,但我希望用户能够一键提交所有表单。

或者,是否有其他库可以做到这一点?

非常感谢

【问题讨论】:

【参考方案1】:

试试这个

<div class="dropzone dz-clickable" id="myDrop">
  <div class="dz-default dz-message" data-dz-message="">
    <span>Drop files here to upload</span>
  </div>
</div>

JS

<script>
    Dropzone.autoDiscover = false;
    Dropzone.default.autoDiscover=false;
    $("div#myDrop").dropzone(
        url: "/file/post",
    );
</script>

【讨论】:

【参考方案2】:

我把 5.7.0 版本的所有 dropzone 源代码都红了,找到了优雅的解决方案。

解决方案

<form id="upload" enctype="multipart/form-data">
    <input type="text" name="name" value="somename">
    <input type="checkbox" name="terms_agreed">
    <div id="previewsContainer" class="dropzone">
      <div class="dz-default dz-message">
        <button class="dz-button" type="button">
          Drop files here to upload
        </button>
      </div>
    </div>
    <input id="dz-submit" type="submit" value="submit">
</form>
Dropzone.autoDiscover = false;
new Dropzone("#upload",
      clickable: ".dropzone",
      url: "upload.php",
      previewsContainer: "#previewsContainer",
      uploadMultiple: true,
      autoProcessQueue: false,
      init() 
        var myDropzone = this;
        this.element.querySelector("[type=submit]").addEventListener("click", function(e)
          e.preventDefault();
          e.stopPropagation();
          myDropzone.processQueue();
        );
      
    );

【讨论】:

【参考方案3】:

这是另一种方法:在您的表单中添加 div 并使用类名 dropzone,并以编程方式实现 dropzone。

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 将尝试附加两次

【讨论】:

这样,他不能使用默认的提交按钮,它不响应他的问题 它是异步的,所以文件无需提交按钮即可保存,提交时他可以将上传的文件名保存到数据库中 但这还是不使用原表单提交 这是我的问题,你解决了,@Satindersingh 这很有帮助,如果您手动设置 url,您可以将任何元素设置为 dropzone。我使用成功处理程序将文件名发布到主要表单中的隐藏/禁用字段。【参考方案4】:

我想在这里提供一个答案,因为我也遇到了同样的问题——我们希望 $_FILES 元素可以作为同一篇文章的一部分作为另一个表单的一部分。我的回答是基于@mrtnmgs 但注意到添加到该问题的 cmets。

首先:Dropzone 通过 ajax 发布其数据

仅仅因为您使用formData.append 选项仍然意味着您必须处理用户体验操作 - 即这一切都发生在幕后,而不是典型的表单发布。数据已发布到您的 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';
        )
    ,
);

【讨论】:

它正在工作,但我正在上传 3 张图片,它只在后端发送 2 张图片。我更改了以下代码:maxFiles: 10, uploadMultiple: true, 为什么它只发送 2 张图片? 我会尝试使用替代图像,看看文件是否有问题。或者,您的 PHP/ASP 等是否会给您带来任何错误?可能是图像太大、磁盘空间不足、服务器限制/设置等。 没有。我的 PHP 没有错误。它正在正确上传图像。但是,我得到了解决方案。我更改了以下几行:url:myURL uploadMultiple:true,parallelUploads:25,maxFiles:25,autoProcessQueue:false,addRemoveLinks:true, 我觉得你的解释很有趣。我无法让元素进入处理页面。我认为这与会话有关。您能否举一个 php 处理页面的示例或说明为什么我的 _FILE 和 _POST 元素没有解析。 只需发布到执行 并通过浏览器控制台 > 网络选项卡(Chrome 中的名称)对其进行分析,然后查看输出。【参考方案5】:

这是我的示例,基于 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>

【讨论】:

【参考方案6】:

我有一个更自动化的解决方案。

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 字符串存储在隐藏的输入字段中。您可以通过标准的base64_decode() 方法将其解码回 PHP 中的二进制字符串。

我不知道这种方法是否会在处理大文件时受到影响,但它适用于大约 40MB 的文件。

【讨论】:

您如何解码和处理将与图​​像一起提交的其他字段的数据? @sam 不需要解码其他字段。它们一开始没有被编码,只有文件被编码。 你能分享一些html,javascript的示例代码以及如何在laravel php中检索。 如果要添加多个图像,您必须删除 html 文件输入并为每个图像添加 quing js $('#fileUpload').append('') 其中 content 是 base64 编码的图像。 这不会可靠地工作,因为输入字段值具有浏览器上限的最大大小。在我的情况下,上传的文件上限为 384kb,因为 base64 字符串不完全适合该值。【参考方案7】:

我遇到了完全相同的问题,发现 Varan Sinayee 的答案是唯一真正解决了原始问题的答案。不过,这个答案可以简化,所以这里有一个更简单的版本。

步骤如下:

    创建一个普通表单(不要忘记方法和 enctype args,因为 dropzone 不再处理这些)。

    class="dropzone"(这就是Dropzone附加到它的方式)和id="yourDropzoneName"(用于更改选项)放入一个div。

    设置 Dropzone 的选项,设置表单和文件发布的 url,停用 autoProcessQueue(因此它仅在用户按下“提交”时发生)并允许多次上传(如果需要)。

    将init函数设置为使用Dropzone而不是点击提交按钮时的默认行为。

    仍在 init 函数中,使用“sendingmultiple”事件处理程序将表单数据与文件一起发送。

瞧!您现在可以像使用普通表单一样在 $_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());
        );
    

【讨论】:

这个解决方案很好用,但它不再重定向到下一页,因为它阻止了默认的提交行为。 @TIIUNDER - 它准备通过 ajax 调用发送表单信息而无需重新加载页面 - 这就是为什么有 e.preventDefault(); @TIUNDER 您可以在成功事件中添加重定向 +1 这似乎是唯一可行的解​​决方案。不用一一做formData.append,还可以$(":input[name]", $("form")).each(function () formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); );(不好意思我不知道怎么把换行符放在这里) 不知道为什么,但我必须使用 $('#myDropzone').dropzone(...) 而不是 Dropzone.options.myDropzone。除此之外,工作得很好。【参考方案8】:

这只是您如何在现有表单中使用 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 +'">');

这假设您有一个 ID 为 #botofform 的 div,这样在上传时您可以使用上传文件的名称。

注意:我的上传脚本返回了上传的文件名.jpeg dubblenote 你还需要制作一个清理脚本来检查上传目录中未使用的文件并删除它们..如果在前端未经过身份验证的形式:)

【讨论】:

这不会将 dropzone 图像与其他表单字段一起提交。您正在做的是正常上传图片,保存图片名称,然后重新提交带有图片名称的其余表单字段。【参考方案9】:

为了在单个请求中提交所有文件以及其他表单数据,您可以将 Dropzone.js 临时隐藏的 input 节点复制到您的表单中。您可以在 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);
);

显然,这是一种取决于实现细节的解决方法。 Related source code.

【讨论】:

我基本都是用这种方式,但是由于明显的处理延迟,最后把文件内容处理挂在myDropzone.on("thumbnail", () =&gt; )事件下。立即在"addedFile" 上进行处理,文件在访问时可能仍为undefined 我正在尝试使用它,它可以将隐藏文件输入字段带到表单中,当我提交时,发布数据显示我的字段files[] 但无论如何它都是空白的我愿意。有任何想法吗?如果有任何不同,请在 Laravel 中进行。 您好!为什么选择的文件会上传,但如果文件被删除则不会(错误 4)?【参考方案10】:

除了 sqram 所说的,Dropzone 还有一个未记录的选项“hiddenInputContainer”。您所要做的就是将此选项设置为您希望隐藏文件字段附加到的表单的选择器。瞧! Dropzone 通常添加到正文的“.dz-hidden-input”文件字段会神奇地移动到您的表单中。无需更改 Dropzone 源代码。

现在虽然这可以将 Dropzone 文件字段移动到您的表单中,但该字段没有名称。所以你需要添加:

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

到 dropzone.js 在这行之后:

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

在 547 行附近。

【讨论】:

【参考方案11】:

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

【讨论】:

感谢您的帖子!解决了我的问题。另一个快速的问题,当没有选择(上传)图像时,这不起作用,如何解决这个问题? 顺便说一句:如果您将控制器操作与模型绑定一起使用并像这样提交表单,则模型将为空。由于某种原因,此方法不会将实际数据绑定到模型。 当 autoProcessQueue=false 时不会触发任何事件 @Sato 在提交按钮的点击事件上,您可以使用 galleryfile.getAcceptedFiles().length 检查 dropzone 上接受文件的长度,如果没有上传文件,您应该提交表单。跨度> @EdwardChopuryan 这与dropzone提交数据的方法无关。问题可能出在您平台上输入标签的“命名约定”上,例如 ASP.Net MVC。【参考方案12】:

Enyo's tutorial 很棒。

我发现本教程中的示例脚本适用于嵌入在 dropzone 中的按钮(即表单元素)。如果您希望将按钮放在表单元素之外,我可以使用点击事件来完成它:

首先,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();
        ); 
    

【讨论】:

您不能在表单中包含表单并提交。 当我尝试这个时,dropzone-previews 容器似乎被忽略了。 Dropzone 只是将预览添加到表单的底部。您需要将 'previewsContainer: '.dropzone-previews',' 添加到您的配置中。 这没有回答原始问题。最初的问题是如何将 Dropzone 与现有表单一起使用,而不是关于触发操作的按钮的位置。【参考方案13】:

您可以通过从 dropzone 捕获“发送”事件来修改 formData。

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

【讨论】:

我喜欢这个答案 - 但它会假设字段名和值已被填充。这是在上传时触发的,这可能在表单提交的单独时间发生。换句话说,您不能假设在发送图像时已填写表格。

以上是关于将 Dropzone.js 与其他字段集成到现有的 HTML 表单中的主要内容,如果未能解决你的问题,请参考以下文章

将聊天模块集成到现有的 ruby​​ 应用程序

将 React Native 代码集成到现有的 android 项目中

如何将 midi 导出集成到现有的 lilypond 分数中

将 ResKit 集成到现有的 Core Data 应用程序中

将 grails 集成到现有的 Spring 应用程序中?

如何将人行横道集成到现有的离子目标c项目中