0 前言
1.项目需要上传文件和大量的文件夹,页面只有一个input file标签会很丑,偶然间得知dropzone类库, 决定使用。 2. 项目后端采用springmvc接收,调用minio代码上传至本地文件数据库。 3. 本以为只是美化一下界面,很快就能实现,没想到中间遇到了太多的问题。 4. 经过两天时间,网上查资料和自己摸索,终于实现了我想要的所有功能 5. 功能包括:单个文件上传、多个文件上传、大量文件夹(上千个)上传、文件和表单一起上传等。 6. 代码中用到jquery
1 dropzone介绍
DropzoneJS是一个提供文件拖拽上传并且提供图片预览的开源类库.
它是轻量级的,不依赖任何其他类库(如JQuery)并且高度可定制.
简而言之,有个强大的文件拖拽功能和好看的界面,网站链接如下:
中文网站
英文网站
GitHub
2 单文件上传
2.1 问题:文件与表单一同提交
刚开始就遇到了问题,dropzone默认情况下文件拖放到指定区域后就自动进行上传,而不需要点击提交,但是我后台需要将一些表单数据和文件一起提交,于是问题出现了。
2.2 解决:点击提交按钮,将文件和表单一起发送
- 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>
- 监听按钮的点击事件,点击后发送数据
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 要用到file
的webkitdirectory
功能(有局限,对部分浏览器如谷歌,兼容)
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 第二种方法,修改源代码
目前网上没有看到类似的处理方式,希望对大家有帮助!
- 通过debug可以看到,后台的文件只有两个属性,有一个是
name
,对应的是前端File
对象的name
属性,我需要将name
用webkitRelativePath
替代 - dropzone文件上传函数入口
processQueue()
,进去
myDropzone.processQueue();
- 找到处理多文件的函数,进去
return this.processFiles(queuedFiles.slice(0, parallelUploads - processingLength));
- 找到提交文件的函数,进去
return this.uploadFiles(files);
- 找到文件发送逻辑
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);
- 发现文件名是通过
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);
- 之后通过
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 最后
贴一下公众号吧!欢迎关注!
今天的文章
springmvc接收文件和参数_springmvc接收文件和参数分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/80131.html