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

214

我目前有一个HTML表单,用户填写希望发布的广告细节。我现在想要能够添加dropzone以上传出售物品的图像。

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

是否可能只在表单的一部分中使用dropzone,即仅指定元素作为"dropzone",而不是整个表单?

我可以使用单独的表单,但我希望用户能够在一个按钮上提交所有内容。

或者,是否有另一个库可以做到这一点?

非常感谢

13个回答

76
这是另一种方法:在你的表单中添加一个类名为 dropzone 的 div,然后通过编程实现 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 将尝试两次附加。

39
因此,他无法使用默认的提交按钮,因为它不能响应他的问题。 - clement
6
但这仍然没有使用原始表格进行提交。 - dangel
3
这是我的问题,而你解决了它,谢谢 @Satindersingh. - Su4p
1
@dangel 我也曾经认为那就是我想要的,但最终的解决方案让我非常满意。我建议你立即提交上传。 - DigitalDesignDj
2
这非常有帮助,如果手动设置URL,您可以将任何元素设置为拖放区域。我使用成功处理程序将文件名发布到主表单中的隐藏/禁用字段中。 - DigitalDesignDj
显示剩余8条评论

60

我曾经遇到同样的问题,并发现Varan Sinayee的回答是唯一解决原问题的答案。不过,那个答案可以更简单,所以这里提供一个更简单的版本。

步骤如下:

  1. 创建一个普通的表单(不要忘记method和enctype参数,因为dropzone不再处理这些内容)。

  2. 在内部放置一个div,其中class="dropzone"(这是Dropzone附加到它的方式),id="yourDropzoneName"(用于更改选项)。

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

  4. 将init函数设置为使用Dropzone而不是默认行为,当单击提交按钮时。

  5. 仍然在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());
        });
    }
}

4
这个解决方案很好,并且有效,但是由于防止默认的提交行为,它不再重定向到下一页了。 - Felix G.
1
@TIUNDER,你可以在成功事件中添加重定向。 - doflamingo
5
+1 这似乎是唯一可行的解决方案。与逐个执行formData.append不同,您还可以执行以下操作: formData.append(this.name, $(':input[name=' + this.name + ']', $("form")).val()); });``` (抱歉我不知道如何在这里换行) - Aximili
我如何为单个图像附加另一个输入文件? - Leoh
1
不确定为什么,但我必须使用 $('#myDropzone').dropzone({...}) 而不是 Dropzone.options.myDropzone。除此之外,运行得很好。 - Sem
显示剩余7条评论

25

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

对于服务器端:

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
谢谢您的帖子!解决了我的问题。还有一个快速的问题,当没有选择图片(上传)时,这个方法不起作用,如何解决? - Sato
顺便提一下:如果您使用带有模型绑定的控制器操作并像这样提交表单,则模型将为空。由于某种原因,此方法不会将实际数据绑定到模型。 - Edward Chopuryan
1
当autoProcessQueue=false时,不会触发任何事件。 - cyril
@Sato 在提交按钮的点击事件中,您可以使用 galleryfile.getAcceptedFiles().length 检查下拉区域中已接受文件的长度。如果没有上传文件,则应提交表单。 - Varan Sinayee
@EdwardChopuryan 这与通过dropzone提交数据的方法无关。很可能问题出在您平台上输入标签的“命名约定”,例如ASP.Net MVC。 - Varan Sinayee

16

我可以为此提供更加自动化的解决方案。

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
@sam 不需要解码其他字段。它们一开始就没有被编码,只有文件被编码了。 - Umair Ahmed
你能分享一些HTML、JavaScript的样例代码以及如何在Laravel PHP中检索它们吗? - sam
2
如果您想添加多个图像,则必须删除HTML文件输入并在每个图像的quing js中添加它$('#fileUpload').append('<input hidden name="files[]" value='+content+'/>'),其中content是base64编码的图像。 - AleXzpm
1
这样做不可靠,因为输入字段的值受浏览器最大大小限制。在我的情况下,由于base64字符串无法完全适应该值,上传的文件被限制在384kb。 - user986408
显示剩余7条评论

8

回应sqram的评论,Dropzone还有一个未记录的选项"hiddenInputContainer"。只需将该选项设置为要附加隐藏文件字段的表单选择器即可。然后,魔术般地,Dropzone通常添加到主体的".dz-hidden-input"文件字段就移动到了您的表单中。无需更改Dropzone源代码。

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

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

在此行后添加dropzone.js:
_this.hiddenFileInput = document.createElement("input");

大约在第 547 行。


8

Enyo的教程非常好。

我发现,在教程中的示例脚本对于嵌入在投放区域(即表单元素)内的按钮效果良好。如果您希望将按钮放在表单元素外面,可以使用点击事件来实现:

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

26
你不能在一个表单里面再嵌套一个表单并进行提交。 - paul
1
当我尝试这样做时,dropzone-previews容器似乎被忽略了。Dropzone只是将预览添加到表单底部。您需要在配置中添加“previewsContainer:'.dropzone-previews'”。 - Aaron Hill
9
这并没有回答原始问题。原始问题是如何在现有表单中使用 Dropzone,而不是关于触发操作的按钮的位置。 - CSSian

6

我希望能在这里做出回答,因为我也曾遇到过同样的问题 - 我们想要将 $_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';
        })
    },
});

它正在工作,但我正在上传3张图片,但后端只发送了2张图片。我已更改以下代码: maxFiles: 10, uploadMultiple: true, 为什么只发送2张图片? - Shehroz Altaf
我建议您尝试使用另一张图片,看看是否是文件的问题。或者,您的PHP/ASP等是否给出任何错误提示?这可能是因为图片太大、磁盘空间不足、服务器限制/设置等原因。 - Antony
不,我的PHP没有错误。它正在正确上传图像。 然而,我找到了解决方案。我更改了以下行: url: myURL uploadMultiple: true, parallelUploads: 25, maxFiles: 25, autoProcessQueue: false, addRemoveLinks: true, - Shehroz Altaf
我觉得你的解释非常有趣。我无法让元素进入处理页面。我认为这与会话有关。你能否给出一个PHP处理页面的例子或指出为什么我的_FILE和_POST元素没有解析? - Rich
只需将代码 <?php print_r($_FILES); print_r($_POST); ?> 发布到一个页面上,然后通过浏览器控制台>网络选项卡(在Chrome中是这样称呼的)进行分析,并查看输出结果。 - Antony

5
您可以通过捕获dropzone的“sending”事件来修改formData。
dropZone.on('sending', function(data, xhr, formData){
        formData.append('fieldname', 'value');
});

2
我喜欢这个答案,但它假定fieldname和value已被填充。这是在上传时触发的,可能会与表单提交发生在不同的时间。换句话说,在发送图像时不能假定表单已经填写完整。 - Antony

5
为了在单个请求中提交所有文件和其他表单数据,您可以将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 - Matti
我正在尝试使用这个方法,它可以将隐藏的文件输入字段添加到表单中。当我提交时,POST数据显示我的字段files[],但无论我做什么,它都是空白的。有什么想法吗?如果使用Laravel,是否会有所不同? - zen
你好!为什么选择文件可以上传,但是如果拖动文件就不行(错误4)? - Ingus
@Ingus 因为隐藏输入框仅用于Dropzone打开文件选择器。上传文件时,它使用自定义AJAX到配置中指定的URL。出于安全原因,我们无法修改<input type="file">的文件属性,因此为了允许修改,我们需要使用AJAX上传。 - Kahan Bhalani

3

适用于5.7.0版本的工作解决方案

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接