使用文件上传
平台要用到一个数据库对比功能,需要上传个模板excel,就开始使用element-ui的el-upload组件。
前端实现
照着https://element.eleme.cn/#/zh-CN/component/upload的文档一顿复制修改,因为要传文件和参数,使用formdata形式上传,Content-Type设置为multipart/form-data,实现如下:
<el-upload
action="/tank/compare/upload"
:on-preview="handlePreview"
:auto-upload="false"
:multiple="false"
accept=".xlsx,.xls"
:on-change="changeFile"
>
<el-button size="small" type="primary">上传对比表</el-button>
<div slot="tip">按模板上传对比表</div>
</el-upload>
<script>
changeFile (file) {
let fd = new FormData()
fd.append('file', file)
fd.append('aid', this.selecta)
fd.append('bid', this.selectb)
fd.append('fileName', file.name)
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios.post('/compare/upload2', fd, config).then(data => {
if (data.code === 200) {
this.$message.info('成功上传')
}
})
}
后端实现
后端直接在controller使用MultipartHttpServletRequest获取文件,转换成File保存和读取,实现如下:
//上传文件
@RequestMapping(value="upload2",method = RequestMethod.POST)
public CommonVO upload2(MultipartHttpServletRequest request){
MultipartFile file = request.getFile("file");
CommonVO cVo = new CommonVO();
SimpleDateFormat sdf = new SimpleDateFormat("/yyyyMMdd/");
String format = sdf.format(new Date());
String realPath = request.getServletContext().getRealPath("/upload") + format;
// 这是个目录
File folder = new File(realPath);
if (!folder.exists()) {
folder.mkdirs();
}
String oldName = file.getOriginalFilename();
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
// 目录文件
File tarFile = new File(folder,newName);
try {
file.transferTo(tarFile);
} catch (Exception e) {
e.printStackTrace();
}
CompareObject co = new CompareObject();
// 获取excel文件内容
co.init(ExcelReadUtil.getSheetByFile(tarFile));
cVo.setCode(200);
return cVo;
}
第一个问题
前后端实现完成,开始调用,直接报错500,请求也不进入controller方法:
java.lang.IllegalStateException: Current request is not of type [org.springframework.web.multipart.MultipartHttpServletRequest]: org.apache.catalina.connector.RequestFacade@3f15fd68
还好,网上碰到该问题的还是挺多,需要在spring增加个配置,以实现spring对multipart/form-data请求直接转换成MultipartHttpServletRequest:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8" />
<property name="maxInMemorySize" value="5242880"/>
</bean>
第二个问题
配置好后,请求成功进入到controller,但是debug发现文件内容为空;
在网上查了一通,有各种说法,开始尝试使用第二种后端实现方式,用HttpServletRequest来做转化:
public CommonVO upload(HttpServletRequest req){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) req;
List<MultipartFile> file = multipartRequest.getFiles("file");
----其他实现
}
但是发现还是为空,问题依旧。
继续在网上找寻答案,因为对springmvc的了解有限,只求知道所以然,不求知道之所以然,开始使用第三种后端实现,去掉spring请求转换配置,使用servletUpload组件来读取参数:
DiskFileItemFactory factoy=new DiskFileItemFactory();
ServletFileUpload sfu=new ServletFileUpload(factoy);
try {
List<FileItem> list = sfu.parseRequest(req);
for(FileItem fi:list){
if(fi.isFormField()){
String s = fi.toString();
}else{
String filename = fi.getFieldName();
tarFile = new File(folder,filename);
fi.write(tarFile);
}
}
}
成功解析到参数list。
第三个问题?
参数成功解析了,可是文件内容还是获取不到,写出来的文件内容为[object Object]。
搜了不少后端问题,都没有相关答案,开始看看是不是前端问题(其实早该想到了);
想到使用postman来调用试试,结果还真的成功写了10K的文件,开始对比postman和浏览器的请求区别,并未发现什么。
继续寻求帮助,找到一个突破口,在stackoverflow有个类似问题,他们也是解析FileItem.isFormField为true,等于说该参数为普通非文件参数,他们给出的答案是需要加上filename,不加默认为普通参数。
有方向是好事情,那么怎么给FormData加上文件名字呢?一个网站说是可以fd.append(‘file’, file, file.name),设置3参数,可是实际运行有js报错:
TypeError: Failed to execute ‘append’ on ‘FormData’: parameter 2 is not of type ‘blob’
前端打印file是什么类型,typeof file,为object,的确是类型错误,继续查找object怎么转file:
var file2 = new File(file, file.name, {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', lastModified: Date.now()})
还是报其他错误;
在各种调试中无意间发现file是object,但是file.raw居然是File类型;

尝试性的用file.raw来传输,
fd.append('file', file.row, file.name)
OK了!!
继续尝试
搞半天原来一直是传错类型了,那么前面说的一定要传filename是否也是问题所在呢,去掉filename调用,也可以!说明问题并不真是少传name引起的,弄巧成拙。
没有真正理解js原理的痛,居然就是一直传错对象引起,过程中也有一些教训,后期一定要多多尝试debug,输出对象来定位问题,不要一味去网上直接寻求答案,可能有,但应该要先有自己的思考!
继续想,之前后端的一二种方法是否可以呢,把去掉的配置加回来,使用MultipartHttpServletRequest解析请求,也是一切正常!这次加个心眼,F12发现请求的内容体也与之前不同:

前面一直是:

真相一直就在眼前,可就一直没发现。。。
待了解
那么还剩下一个问题,on-change(file)是官方方法,名字也是file,为什么类型就是object,而file.raw才是真正的File文件呢,raw是个什么鬼,在js中特殊定义吗?
不解,需要看vue原码了(level还不够),如果有大佬刚好在看,可否帮忙解答下,多谢!!
参考文档:
Element Upload上传组件调接口时踩坑
servlet完成form格式下的文件 文本信息上传
SpringMVC中servletFileUpload.parseRequest(request)解析为空获取不到数据问题
本文记录了在Vue项目中使用el-upload组件进行文件上传时遇到的前端和后端问题,包括500错误、文件内容为空、文件类型错误等,并详细描述了解决过程和解决方案。通过调整前端FormData的构造方式,以及后端处理请求的不同方法,最终成功实现了文件上传。
1万+

被折叠的 条评论
为什么被折叠?



