多文件上传
上面讲的是单文件上传,很多时候你可能遇到的需求不光光是单文件上传。就比如你一定熟悉这个页面:
如上你可以看到,这么一次文件上传不止一个图片,并且数量也不确定,但都属于同一名称和集合的内容。这就是多文件上传。对于这种情况无论在前端还是服务端也是很容易处理的。
前端设计
我们这里实现一个多张图片的上传,首先在static目录下创建一个index2.html的页面。里面的具体内容为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>多文件上传</title> </head> <body> <h2>同一类别多个文件上传</h2> <form name="onfile" action="onfiles2" method="post" enctype="multipart/form-data"> 图片: <input type="file" name="img"><br> <input type="file" name="img"><br> <input type="file" name="img"><br> <input type="file" name="img"><br> <input type="submit" value="提交"> </form> </body> </html>
这样前端页面就编写完成,其中action要改为onfiles2,也就是待会要在服务端编写的接口。还有注意的这些input 所有type为file代指类型为文件,而name均为img意思是上传一组名称为img图片的集合。
服务端设计
而在我们服务端,其实用MultipartFile[]数组就可以对这样的多文件进行接收,我们在controller中编写以下代码:
@PostMapping("onfiles2") @ResponseBody public String onfiles2(MultipartFile img[]) throws IOException { for(int i=0;i<img.length;i++) { if(!img[i].isEmpty())//文件不空 { File imgfile =new File("F:/fileupload/"+img[i].getOriginalFilename()); imgfile.createNewFile(); img[i].transferTo(imgfile); logger.info(img[i].getOriginalFilename()); } } return "sucucess"; }
这个处理方式和前面的很相似,只不过是需要遍历MultipartFile[]
对每个文件进行接收处理,当然文件为空的时候不进行处理。
运行测试
这样打开浏览器输入:http://localhost:8080/index2.html
,上传文件测试效果:
这样一组类似相册上传的功能就完成啦,当然实际开发中的文件上传的要求肯定比这个要求严格很多,可能对文件的格式、大小都有一定的要求,这就要求你在前端和服务端都要对文件的后缀名、大小等信息进行校验,以达到自己场景化的需求。
文件下载
文件下载估计你在日常生活中会经常遇到,而你下载的其实就是服务端(服务器)的资源,对于文件类型有多种多样的,浏览器也能够识别很多种资源,事实上你现在访问的这个网页也是服务端的html文件、图片文件等资源,只不过这些资源浏览器能够显示而不会保存到本地。
直接访问资源VS下载资源
如果直接访问的资源是浏览器所不能识别解析的,例如doc、zip等类型文件,那访问的时候会默认下载到本地。而当你在Springmvc中使用下载功能时,无论是什么资源都以下载的形式返回给客户端。这种区别可以参考下图:
在文件下载方面的实现,servlet本身也是实现文件下载的,不过使用起来有点繁琐。其原理就是往HttpServletResponse response的输出流写字节内容。而我们Springmvc对文件下载也做了封装,将下载功能封装至ResponseEntity类中,我们在使用的时候也很方便。下面就来实战文件下载的功能。
首先,我们在F盘建立download文件夹,在里面添加对应文件,这个文件夹我们作为服务端的资源。
前端设计
我们在创建一个文件下载的前端页面,在static目录下创建index3.html
,页面的具体内容为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Springmvc文件下载</title> </head> <body> <h2>Springmvc文件下载</h2> 个人照片<a href="/download/个人照片.png">个人照片.png</a><br> 个人简历<a href="/download/个人简历.pdf">个人简历.pdf</a> </body> </html>
其中href是下载的超链接,download是下载的接口名,而链接最后面部分则是下载资源名称。
服务端设计
文件下载的原理就是服务端向客户端返回二进制流和信息,而Springmvc通过ResponseEntity完成。我们在controller中编写以下接口实现下载的功能:
@GetMapping("download/{filename}") public ResponseEntity<byte[]>download(@PathVariable String filename) throws IOException { //下载文件的路径(这里绝对路径) String filepath= "F:/download/"+filename; File file =new File(filepath); //创建字节输入流,这里不实用Buffer类 InputStream in = new FileInputStream(file); //available:获取输入流所读取的文件的最大字节数 byte[] body = new byte[in.available()]; //把字节读取到数组中 in.read(body); //设置请求头 MultiValueMap<String, String> headers = new HttpHeaders(); headers.add("Content-Disposition", "attchement;filename=" + file.getName()); //设置响应状态 HttpStatus statusCode = HttpStatus.OK; in.close(); ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(body, headers, statusCode); return entity;//返回 }
这样就是实现了文件下载功能,如果用传统servlet的方式下载文件可能需要在HttpServletResponse response中设置各种信息,而使用Springmvc的ResponseEntity只需要将文件二进制主体、头信息以及状态码设置好即可进行文件下载,在易用性和简洁上更胜一筹。
运行测试
打开浏览器输入:http://localhost:8080/index3.html
;点击需要下载的文件,就实现了文件下载的功能,运行情况图如下:
此时你就遇到了一个文件下载非常常见的问题:中文文件名错误显示。这个解决方案也很容易解决,只需将Content-Disposition内容后面的文件名进行url编码即可,具体代码为(替换上面对于部分):
headers.add("Content-Disposition", "attchement;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
这样重启程序,刷新页面再次点击下载的链接,你就会发现文件被成功的下载了:
总结与拓展
至此,Springmvc的单文件上传、多文件上传以及文件下载你已经全部掌握了,是不是满满的成就感想去实现一个自己的小网站并把相关内容放进去?不过Springmvc文件上传下载虽然简单,但你依然需要掌握其原理,学好java中的io文件传输,这样在各种场景的文件传输任务中方能胜任。
总结
前面所讲文件上传,前端就是form表单用<input type="file">表示客户端要上传文件,而服务端主要使用MultipartFile或者MultipartFile[]分别接收单个文件和多个文件。而在存储到本地也仅仅需要在本地磁盘创建对应文件然后MultipartFile调用transferTo()方法即可将上传的文件储存。
而文件下载的前端需要一个请求的url链接,服务端需要编写这个链接对应的接口。通过一些名称找到文件在本地真实的位置通过ResponseEntity即可将二进制文件返回给客户达到文件下载的功能。而ResponseEntity使用也很简单在创建时候只需要传入二进制主体、头和状态码即可成功返回,而这些Springmvc已进行了很好封装你可以直接使用。
而无论是文件上传、多文件上传还是文件下载,一个完整的案例大致都需要这样一个过程:
构思需求和页面大体样式
编写前端html页面
编写服务端响应的请求
启动程序运行测试
在其中过程如果有问题可以根据编译器的错误提示、运行时的错误日志找到根源进行修正,这样完整的案例就可以成功完成啦!
案例拓展
你是否觉得自己掌握的可以了?那好,咱们拓展提升一下,我给你来一个需求:单文件和多文件混合上传
假设小明需要实现一个文件上传功能,小明需要上传一份简历和若干份照片(小于3)。这个项目该如何设计呢?它的计划页面可能是这样的:
我觉得聪明的你一定不会被难住,对于前端界面的html有什么想法呢?
对于种类来说有简历和照片两种文件,对于它们各自来说,简历只有一份,而照片可能有多份。
那么咱们的html页面可以这样设计:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>个人信息上传</title> </head> <body> <h2>个人信息上传</h2> <form name="onfile" action="infoupload" method="post" enctype="multipart/form-data"> 姓名:<input type="text" name="name" ><br> 年龄:<input type="text" name="age"> <br> 图片:<input type="file" name="img"> <input type="file" name="img"> 简历:<input type="file" name="resume"><br> <input type="submit" value="提交"> </form> </body> </html>
这里面和前面的单文件上传不同的是有多个input标签,此外action也要改成infoupload
意思是你需要写这么一个接口来处理这个文件上传的内容。在controller中编写以下代码:
private static Logger logger= LoggerFactory.getLogger(uploadController.class); @PostMapping("infoupload") @ResponseBody public String onfile(String name,String age, MultipartFile img[],MultipartFile resume) throws IOException { logger.info(name);//日志中打印传输的name logger.info(age); //接收img[] for(int i=0;i<img.length;i++) { if(!img[i].isEmpty())//文件不空 { File imgfile =new File("F:/fileupload/"+img[i].getOriginalFilename()); imgfile.createNewFile(); img[i].transferTo(imgfile); } } //接收resume File resumefile =new File("F:/fileupload/"+resume.getOriginalFilename()); //在磁盘中创建文件,此时文件存在但没有内容 resumefile.createNewFile(); //将接受的文件复制到创建的文件中 resume.transferTo(resumefile); return "sucucess"; }
这个理解起来其实也很容易,这个和上面主要的区别就是函数中的多参数,其实每一个参数都是要和前端页面的form表单input标签的内容对应(名称一致)。form表单中的file类型在Springmvc的controller中就是对应MultipartFile类型,form表单中的text类型对应controller中的String类型。如果上传单个文件,在服务端就用MultipartFile类型参数接收,如果多文件就用MultipartFile[]进行接收。上传类型和个数根据你自己的需求设计定义。
我们启动程序打开浏览器输入http://localhost:8080/index4.html选择文件进行上传,然后在本地你可以看到文件成功被保存。
至此,本篇的内容就结束了,本文主要简单讲解了Springmvc中文件上传、多文件上传、文件下载的实现,现在你已熟练掌握。青山不改,绿水长流,我们下期再见!下课!