网站首页 > 技术文章 正文
引言
星期四的时候,我遇到了一个文件上传的问题,与以往不同的是,这一次上传的是多个文件,而且涉及到了久违的javascript代码。(久违是因为前后端分离的趋势,好久没有碰过JS)
虽然最后实现的差强人意,不过也算是完成了功能,接下来就把我发现的一些问题和总结记录一下。
HTML部分展示
<form id ="tempFile" method="post" enctype="multipart/form-data"> <!-- 指令模板文件上传 --> <div style="text-align: center;"> <div class="clearfix" style="width:400px;margin:0 auto;"> <input id ="templatesFile" type="file" multiple="multiple" /> </div> </div> </form>
上述代码可以看到,只是一个简单的 form表单,里面嵌套了一个 input 输入框,这个 input 输入框的 type 类型是file,值得注意的是,如果是允许上传多个文件,那么input输入框必须添加multiple="multiple" 属性。
这里要说明一点,由于使用 submit 按钮提交 form表单会造成页面刷新,因此,现在一般都采用按钮与 form 表单分离的方式,使用 AJAX来异步上传数据。 如下所示:
<button class="btn btn-default" onclick="sendTemplates();" title="upload"> 上传模板文件 </button>
JS代码展示
/** * 上传模板文件 * @returns */ function sendTemplates() { // 需要上传的文件 // var templates = document.getElementById("templatesFile").files; var templates = $("#templatesFile")[0].files; if (templates.length > 2) { alert("上传失败,一次最多两个文件!"); return; } var formdata = new FormData(); for (var i = 0 ; i < templates.length ; i++) { formdata.append("temp" + i, templates[i]); } $.ajax({ url : "/manager/uploadTemplates", type : "POST", data : formdata, processData : false, contentType : false, success : function(resultMap) { if (resultMap.code == "OK") { alert("上传成功!"); } else if (resultMap.code == "FAIL") { alert(resultMap.msg); } }, error : function(e) { alert("服务器异常"); } }); }
代码已经保留了关键的代码部分,剔除了一些无关紧要的操作。
Java代码实现
@RequestMapping(value = "/uploadTemplates", method = RequestMethod.POST) @ResponseBody public Map<String, Object> uploadTemplates(HttpServletRequest request) { MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; List<MultipartFile> templatesList = new ArrayList<>(); for (int i = 0 ; i < 2 ; i++) { templatesList.addAll(multipartRequest.getFiles("temp" + i)); } MultipartFile[] templates = new MultipartFile[templatesList.size()]; templatesList.toArray(templates); Map<String, Object> result = templateService.uploadTemplates(templates); return result; }
看到上面的代码,我不得不说,真的和 shi 一样。不过目前我还没找到很好的办法。
好在 for 循环的 getFiles(...) 如果取不到值的话,就会返回一个空的 List 不会直接抛出异常。
以上就是所有关键的代码部分,下面来说一说调试过程中我使用过的几种方法,以及觉得很坑的地方。
第一坑:jQuery取files要加个[0]
回看js 部分的代码,相信你已经注意到了:
var templates = $("#templatesFile")[0].files;
而FileList 并不是一个数组。如果希望将它转化成一个数组,可以使用 Array.from(FileList)
不过不论是 FileList还是数组,都不是重点,重点是$("#templatesFile")[0] 一定要加上后面的这个 [0] 。而如果使用document.getElementById("...")就不必加 [0]。 这是因为 files 属性是原生js属性,而不是jQuery属性,因此需要通过这个 [0] 将jQuery对象转化为 js对象,才能通过 .files 获得这个FileList !
第二坑:$.ajax 的data域要怎么写以及controller如何接收
说实话,前面的 js 与 java部分的代码实在是迫不得已,不过依然没有找到恰当合适的方法。
这个问题困扰了我整整一天的时间,从星期四的下午开始研究一直到第二天星期五的上午才勉强以上面的代码跑通功能。
之前做过上传单个文件的功能。那天我乐观的以为,只要前端能够获取到 files 数组,然后后端的接口使用 MultipartFile[] 来接收就可以大功告成了!像这样:
@RequestMapping(value = "/uploadTemplates", method = RequestMethod.POST) @ResponseBody public String uploadTemplates(MultipartFile[] templates) { // ...some codes }
但是前端的代码我试过N种方法依然无法成功接收到这个文件数组!不论是这样:
还是这样:
亦或是将数组转化成String,然后后台用String接收:
都 完!全!没!用!
明明以前传其他类型的参数是OK的啊!这到底是为什么?
于是看到了 FormData 的解决方案。但是事情依然并!不!简!单!
一开始我的思路是封装一个FormData对象,然后把文件数组整体放入到一个value中,并且指定一个key ,这样后台通过 getFiles() 方法就可以获得整个文件列表了。
然鹅,并无卵用!
在网上找了下原因,看到了一些别人的代码,全都是每一个key放一个文件!原因是:
简单的说, String、Blob、File可以被FormData传输(注意这里是单个File类型的文件),如果是其他类型,则会被转化成字符串,然而,对于FileList又会是怎样的情况呢?我找到了下面这句话:
Using the FormData API is the simplest and fastest, but has the
disadvantage that data collected can not be stringified.
翻译过来就是,使用FormData最简单,也最高效,但是有一个缺点是,数据集合(比如List或者数组)无法被序列化为字符串。
简直就像是在耍人一样!我真的崩溃了!
也就是说,我必须将文件列表(或者数组)中的每一个文件取出来,分别给每一个文件指定一个Key才能成功的通过formdata传输到后台并接收。
所以才有了这样的代码:
// JS var formdata = new FormData(); for (var i = 0 ; i < templates.length ; i++) { formdata.append("temp" + i, templates[i]); }
和这样的代码:
// java controller public Map<String, Object> uploadTemplates(HttpServletRequest request) { MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; List<MultipartFile> templatesList = new ArrayList<>(); for (int i = 0 ; i < 2 ; i++) { templatesList.addAll(multipartRequest.getFiles("temp" + i)); } // some other codes... }
到目前为止,还没有找到更加简洁和高效的代码,有哪位全栈的哥们可以告诉我吗?
欢迎评论区留言哦!非常感谢!
---欢迎关注【Java圣斗士】,我是你们的小可爱(?ω?) Morty---
---专注IT职场经验、IT技术分享的灵魂写手---
---每天带你领略IT的魅力---
---期待与您陪伴!---
猜你喜欢
- 2024-10-23 「jQuery-5」 JavaScript对象和json
- 2024-10-23 超级好用的轻量级JSON处理命令jq(jsonpath js)
- 2024-10-23 $.ajax()常用方法详解(推荐)(ajax详细讲解)
- 2024-10-23 Jquery相关(jqueryui)
- 2024-10-23 jQuery知识点总结(jquery的认识)
- 2024-10-23 前端工程师面试题汇总(前端工程师面试题汇总怎么写)
- 2024-10-23 jQuery基础教程学习笔记(二)样式操作
- 2024-10-23 前端基础进阶(十三)详细图解jQuery扩展jQuery插件
- 2024-10-23 jQuery对象和DOM对象互相转换(jquery对象怎么转换dom对象)
- 2024-10-23 想学jQuery要了解的10个知识,干货!
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)