最近在做微信端的文件上传,遇到了几个问题,折腾完后这里统一总结下,希望对其他人能有点帮助。
我前端未使用其他上传组件,用的原生的HTML、JS、CSS。
废话不多说,直接罗列你可能关心的点:
前端:
1.html自带file控件样式丑陋,如何达到UI设计要求效果;
2.选择图片后,按照一定的样式回显示图片如何实现;
3.无刷新提交;
4.form序列化无法提交二进制文件(这里是针对Query ajax的serialize()而言),该如何提交文件类型;
5.JS选择器获取单个文件、“multiple”类型多文件及非“multiple”类型的多个同类型input控件文件;
后端:
6.如何在后端接收文件类型(这里特指spring框架)
7.文件传入为空后端报错如何解决;
8.如何在后端接收一组文件;
9.写入文件;
实现(以下实现中,只截取了必要的代码)
问题1: html自带file控件样式丑陋,如何达到UI设计要求效果;
html默认的file控件样式往往达不到我们的要求,所以需要采用一定的css样式达到UI设计的效果。
我们先看下没有添加任何样式的file类型input控件的样式:
我们期望的样式,for example:
实现的HTML、CSS:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css"> html,body{ margin:0; padding:0; } img{ width: 100px; height: 100px; border:1px dotted blue; } input{ width:100px; height:100px; z-index: 999; opacity: 0; position: absolute; left: 0; top: 0; } </style>
</head>
<body>
<img id="preview" src="http://avatar.csdn.net/E/A/1/3_irokay.jpg" alt="">
<input id="file" type="file" name="file">
</body>
</html>
效果图:
请自行把我的头像脑补成UI设计中的图片 - -!重点在于opacity、z-index、positon,然后调整位置使之大小、位置重合即可。
问题2: 选择图片后,按照一定的样式回显示图片如何实现;
我们采用FileReader对象的readAsDataURL回显图片,下面是介绍:
FileReader对象的readAsDataURL方法可以将读取到的文件编码成Data URL。Data URL是一项特殊的技术,可以将资料(例如图片)内嵌在网页之中,不用放到外部文件。使用Data URL的好处是,您不需要额外再发出一个HTTP 请求到服务器端取得额外的资料;而缺点便是,网页的大小可能会变大。它适合应用在内嵌小图片,不建议将大图像文件编码成Data URL来使用。您的图像文件不能够超过浏览器限定的大小,否则无法读取图像文件。
JS代码:
<script type="text/javascript"> function previewImg(){ let preview = document.querySelector('img[id=preview]'); let file = document.querySelector('input[id=file]').files[0]; let reader = new FileReader(); reader.onloadend = function () { preview.src = reader.result; } if (file) { reader.readAsDataURL(file); } else { preview.src = ""; } } </script>
问题3: 无刷新提交
这里采用原始的ajax提交,jquery ajax提交见我的另外一篇博客:
http://blog.csdn.net/irokay/article/details/72984048
步骤:
1)let request = new XMLHttpRequest()
2)request.open(“POST”, url)
3)request.onload = function(oEvent) {
if (request.status == 200) {
//alert(“success”);
} else {
//alert(“出错啦!请稍后再试。” + request.status);
}
};
4) request.send(formData);
问题4: form序列化无法提交二进制文件(这里是针对Query ajax的serialize()而言),该如何提交文件类型;
若采用Jquery ajax,序列化(serialize())表单是无法序列化二进制数据的,故我们采用FormData的方式提交文件类型。FormData对浏览器版本的要求见上面博客地址。当然我们这里没有采用jquery封装的ajax提交。
注:我这里是移动开发,移动端浏览器内核版本还是比较高的,所以使用FormData无问题。
FormData的使用:
let formData = new FormData();
//FormData添加文件类型:
formData.append("file",document.querySelector('input[id=file]').files[0]);
问题5: JS选择器获取单个文件、“multiple”类型多文件及非“multiple”类型的多个同类型input控件文件;
这里重点说一下。
1)单个文件的选择上述已经写到:
document.querySelector('input[id=file]').files[0]
2)若是“multiple”类型的input控件,即一个input控件可以同时选择多个文件(<input type=”file” multiple=”multiple” />);
选择器获取的写法:
<input id="file" type="file" multiple="multiple" />
//获取一个多选input file控件上的文件
document.querySelector('input[id=file]').files;
//alert("length:" + document.querySelector('input[id=file]').files.length);
3) 我们考虑下另外一种方式,采用追加的方式实现多图预览,譬如有多个id同为file的input组件,且全是单选(非multiple),这些照片为一组,需要统一获取并上传。
选择器获取方法为:
let files = document.querySelectorAll('input[id=file]');
let formData = new FormData();
for(let i = 0; i < files.length; i++){
//alert("路径为:" + files[i].value);
//alert("file对象:" + files[i].files[0]);
//将一组文件类型追加至file集合中
formData.append("file",files[i].files[0]);
}
这样即可将一组文件加入FormData的同一key下,一同提交。
问题6: 如何在后端接收文件类型(这里特指spring框架)
//接收单个文件
//且file文件不可为空,否则报错
public void savePic(@RequestParam("file") CommonsMultipartFile file,HttpServletRequest request, HttpServletResponse response,) throws IOException {...}
问题7:文件传入为空后端报错如何解决;
若前端未传入任何文件,上述写法会报错:
Cannot convert value of type [java.lang.String] to required type [org.springframework.web.multipart.commons.CommonsMultipartFile]
当前端传入文件可能为空时,后端接收不可写入方法参数中,避免报错。正确写法如下:
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartRequest.getFile("file"); //获取单个文件
//为空判断以及处理等...
问题8: 如何在后端接收一组文件;
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
//MultipartFile file = multipartRequest.getFile("file"); 获取单个文件
List<MultipartFile> fileList = multipartRequest.getFiles("file"); //获取一组文件
if(null != fileList && fileList.size()>0){
for(int i = 0; i < fileList.size(); i++){
System.out.println("第(" + i + ")个文件名:" + fileList.get(i).getOriginalFilename());
}
}
问题9: 写入文件;
File newFile=new File(path);
//通过CommonsMultipartFile的方法直接写文件
file.transferTo(newFile);