Dropzone单文件上传、多文件上传、文件夹上传,springmvc接收,上传至Minio的一系列问题

0 前言

1.项目需要上传文件和大量的文件夹,页面只有一个input file标签会很丑,偶然间得知dropzone类库,
决定使用。
2. 项目后端采用springmvc接收,调用minio代码上传至本地文件数据库。
3. 本以为只是美化一下界面,很快就能实现,没想到中间遇到了太多的问题。
4. 经过两天时间,网上查资料和自己摸索,终于实现了我想要的所有功能
5. 功能包括:单个文件上传、多个文件上传、大量文件夹(上千个)上传、文件和表单一起上传等。
6. 代码中用到jquery

1 dropzone介绍

DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库.
它是轻量级的,不依赖任何其他类库(如JQuery)并且高度可定制.

在这里插入图片描述

简而言之,有个强大的文件拖拽功能和好看的界面,网站链接如下:
中文网站
英文网站
GitHub

2 单文件上传

2.1 问题:文件与表单一同提交

刚开始就遇到了问题,dropzone默认情况下文件拖放到指定区域后就自动进行上传,而不需要点击提交,但是我后台需要将一些表单数据和文件一起提交,于是问题出现了。

2.2 解决:点击提交按钮,将文件和表单一起发送

  1. dropzone有两种穿件文件拖放区域的方式,一种是<form>,一种是<div>,既然要和其他数据一起提交,表单中嵌套表单是不行的,于是采用<div>的形式,下面是页面:
 <form class="form-horizontal" action="" method="post" enctype="multipart/form-data">           
	<input class="form-control" value="default" type="text" name="folderName" id="folderName"> 
 	<textarea class="form-control" rows="5" name="siteDescription" id="siteDescription"></textarea>
 <!--Start row-->
	<div id="myDropzone" class="dropzone form-control"></div>
  <!-- end row -->
   <button type="submit" class="btn btn-primary" id="submit">Submit</button>
</form>
  1. 监听按钮的点击事件,点击后发送数据
    2.1 首先需要禁用自动查找,采用Dropzone.autoDiscover = false
    2.2 取消自动提交,autoProcessQueue : false,
    2.3 文件的名称设置,相当于input标签的name属性,默认是file,注意名称需要和后台一致
    2.4 关闭多文件上传,uploadMultiple: false否则会拿不到文件,这个下面会解释
    2.5 监听按钮点击事件,阻止默认事件,若区域内有文件,则调用myDropzone.processQueue();方法提交,如下图
    2.6 监听sending方法,当文件发送时,将其他表单数据一起发送,如下图

监听按钮点击事件

 $("#submit").on("click", function (e) {
                e.preventDefault();
                e.stopPropagation();
                if (myDropzone.getAcceptedFiles().length !== 0) {
                    myDropzone.processQueue();
                }
        });

将其他表单数据一起发送

this.on("sending", function (data, xhr, formData) {
            formData.append("folderName", $("#folderName").val());
            formData.append("siteDescription", $("#siteDescription").val());
        });

下面是完整的js代码

Dropzone.autoDiscover = false; //取消自动提交
var myDropzone = new Dropzone("#myDropzone", {
    url: "/file/upload", //需要上传的后台接口地址
    method:"post",
    dictDefaultMessage: '拖动文件至此或者点击上传', // 设置默认的提示语句
    paramName: "siteFile", // 传到后台的参数名称
    autoProcessQueue: false, //关闭自动上传功能,默认会true会自动上传,也就是添加一张图片向服务器发送一次请求
    uploadMultiple: false,
    parallelUploads: 5,
    maxFiles: 5,
    maxFilesize: 1,
    addRemoveLinks: true,
    dictFallbackMessage: '不好意思,您的浏览器不支持!', //如果浏览器不支持,默认消息将被替换为这个文本。默认为 “Your browser does not support drag'n'drop file uploads.”。
    dictInvalidFileType: '该文件不允许上传', //如果文件类型不匹配时显示的错误消息。
    dictResponseError: '上传失败,请稍后重试', //如果服务器响应是无效的时候显示的错误消息。 {{statusCode}} ` 将被  替换为服务器端返回的状态码。
    init: function () {
        var submitButton = $("#submit")
        myDropzone = this; // closure
        //为上传按钮添加点击事件
        submitButton.on("click", function (e) {
                e.preventDefault();
                e.stopPropagation();
                if (myDropzone.getAcceptedFiles().length !== 0) {
                    myDropzone.processQueue();
                }
        });
        this.on("sending", function (data, xhr, formData) {
            formData.append("folderName", $("#folderName").val());
            formData.append("siteDescription", $("#siteDescription").val());
        });
        this.on("success", function (file, data) {
            // 上传成功触发的事件
            //弹窗提示
            swal("上传成功!", file.name,"success")
        });
    }
});

这个问题在GitHub上有例子,这也是后来才知道的,具体链接见
文件和表单一起提交(GitHub)

3 多文件上传

问题:多文件上传时文件参数获取不到

3.1 多文件上传时,有几个参数需要提前了解下

	paramName: "siteFile", // 传到后台的参数名称
    uploadMultiple: true, //开启多文件上传
    parallelUploads: 5, //并行上传文件数量,最大为8
    maxFiles: 5, //最大提交文件数量,为null时,则全部提交
    maxFilesize: 3, //文件大小,以MB为单位

3.2 springmvc接受参数配置:

@RequestParam(name = "siteFile") MultipartFile file,

3.3 提交后,springmvc无法接收到相同文件名的文件,导致文件上传失败,如下图
在这里插入图片描述

解决:详细过程如下

3.4 提示请求错误,然后去找请求的问题,后面发现请求头的Form Data是这样的:
在这里插入图片描述
可以看到,多文件上上传时附带的是两个文件,而且是数组的形式,所以springmvc找不到到名为siteFile的文件,于是报错了

3.5 网上找了一种解决办法,将siteFile改成siteFile[],验证之后,发现还是无法接收到参数

3.6 后来通过修改源代码的形式进行解决,将siteFile[0] siteFile[1]...全改为siteFile,这样后台自然就能接收了,修改如下

return "" + this.options.paramName + (this.options.uploadMultiple ? "[" + n + "]" : "");
改为
return "" + this.options.paramName + (this.options.uploadMultiple ? "" : "");

文件成功上传!
在这里插入图片描述
在这里插入图片描述

这个问题困扰我很久,现在也算是圆满解决了!

4 文件夹上传

问题:项目要使用文件夹上传功能,dropzone默认是文件上传

解决:如下

4.1 要用到filewebkitdirectory功能(有局限,对部分浏览器如谷歌,兼容)
4.2 在dropzone的init()函数中添加如下代码,开启文件夹选择

 init: function () {
 	this.hiddenFileInput.setAttribute("webkitdirectory", true);
 }

4.3 将文件夹中的所有文件上传,需要开启上述的多文件上传功能uploadMultiple: true,同时修改源代码
4.4 后台接收

@RequestParam("siteFiles") List<MultipartFile> files

5 文件夹上传时路径问题

问题:需要保留原来的目录结构

采用dropzone上传时,获取的文件名不带相对路径,如file/a/b.txt,只能获取到b.txt,无法获取前面的路径。

但是我需要保留原有的文件结构

解决:如下

5.1 第一种方法:当文件发送时,将文件的相对路径一起通过参数的形式,通过formData.append()方法绑定到表单上。
文件的相对路径在File对象的webkitRelativePath中,如下
在这里插入图片描述
将路径存储到数组上:

 init: function () {
	let webkitdirectorys = [] //定义一个相对路径的数组
    this.on("addedfile",function(file){
      	//每当添加文件时,就给该数组添加路径
		webkitdirectorys.push(file.webkitRelativePath) 
     });
     this.on("sending", function (data, xhr, formData) {
     	//发送表单时,将路径添加进去
     	formData.append("webkitdirectorys ", webkitdirectorys ); 
     });
 }

最后,文件路径也保留了,文件也上传成功了。

但是有个缺陷,就是webkitdirectorys中的路径和文件无法一一对应,可能会出问题,同时,文件数量非常多时,表单参数会非常多,可能会降低传输效率?

有强迫症的我不想看到表单传一大堆数据到后台!!!

5.2 第二种方法,修改源代码

目前网上没有看到类似的处理方式,希望对大家有帮助!

  1. 通过debug可以看到,后台的文件只有两个属性,有一个是name,对应的是前端File对象的name属性,我需要将namewebkitRelativePath替代
  2. dropzone文件上传函数入口processQueue(),进去
myDropzone.processQueue();
  1. 找到处理多文件的函数,进去
return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
  1. 找到提交文件的函数,进去
return this.uploadFiles(files);
  1. 找到文件发送逻辑
for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) {
         formData.append(this._getParamName(i), files[i], files[i].name);
  }
  return xhr.send(formData);
  1. 发现文件名是通过formData.append()方法进行追加的,修改它的文件名
 for (i = _m = 0, _ref5 = files.length - 1; 0 <= _ref5 ? _m <= _ref5 : _m >= _ref5; i = 0 <= _ref5 ? ++_m : --_m) {
        //by xinlei
        //将name用webkitRelativePath替换
        let name = files[i].name
        files[i].webkitRelativePath ? name = files[i].webkitRelativePath: true
        formData.append(this._getParamName(i), files[i], name);
        // formData.append(this._getParamName(i), files[i], files[i].name);
      }
      return xhr.send(formData);
  1. 之后通过getOriginalFilename()方法获取的文件名,就是webkitRelativePath了,如file/a/b.txt,之后在进行处理即可!

6 上千个文件上传

问题:dropzone多文件上传最多同时能上传8个文件

注意:当maxFiles:null时,可以上传该文件夹下的所有文件!

若文件超过8个,就无法一次性上传,而需要再次点击提交按钮,才会继续上传剩下的文件。
如果我需要上传大量的文件,不可能每次点击提交按钮!

解决:事件监听

监听success事件,当文件上传成功后,就会调用该事件,
每当有文件上传成功后,就继续调用方法上传队列中的文件,直到队列中的文件为空,如下

 this.on("success", function (file,data){
            if (myDropzone.getQueuedFiles().length !== 0) {
                myDropzone.processQueue();
            }else {
                swal("上传成功!", file.name, "success")
            }
        })

测试时成功的!

7 多文件删除

选择了文件夹下的所有文件,不可能一个个删除,现需要一个按钮来将区域内的文件全部清除,如下

//为删除按钮添加事件
        removeButton.on("click", function(){
            myDropzone.getAcceptedFiles().forEach(element => {
                myDropzone.removeFile(element)
            })
            myDropzone.hiddenFileInput.setAttribute("webkitdirectory", true);
        });

注意当文件清除完成后,控件中的webkitdirectory属性会消失,需要重新添加!

8 总结

1. 本来以为能够轻松解决的问题,却花费了我两天的时间,还是值得纪念一下的
2. 当然也收获很多,最大的收获就是问题解决后的开心
3. 同时也明白了太多地方的不足,以及面对问题缺乏冷静的思考能力
4. 加油

9 最后

贴一下公众号吧!欢迎关注!

你好

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值