SpringMVC实现文件的上传和下载 相对于JavaWeb阶段咱们使用过servlet实现文件上传和下载操做;而SpringMVC实现了对上传操做的直接支持,提供了multipart
解析器。MultipartFile
提供了一些对文件操做的方法,使得文件上传变得更简单。不管上传仍是下载都是进行二进制流的转换,下面咱们以案例的形式了解一下如何使用SpringMVC实现文件的上传操做。html
<!--more-->java
enctype
设置为multipart/form-data
,每一个输入域都将做为POST请求的不一样部分进行提交(默认提交的表单中数据存储格式是名字-值
,显然是不适合相似文件上传那种二进制数据的)。multipart
数据的解析器CommonsMultipartResolver
(MultipartResolver
接口的实现类),可是这个解析器是基于Apache Commons FileUpload
技术的,因此须要commons-filrUpload.jar
支持。除了咱们以前使用的Spring以及SpringMVC的先关jar依赖包,还须要导入如下jar文件:git
commons-fileupload.jar commons-io.jar
首先咱们看一下项目结构:github
注意:这里我使用的配置是:IDEA + tomcat + Maven,对于maven项目咱们要清楚项目编译后的文件都放在target
目录下。web
save.jsp
<%@ page isELIgnored="false" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path; %> <html> <head> <title>Title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h2>表单</h2> <form action="<%=basePath%>/user/save" method="post" enctype="multipart/form-data"> username: <input type="text" name="username"/><br/> password: <input type="password" name="password"/><br/> profile image:<input type="file" name="image"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
解释: 注意在上面<%%>
中的Java代码是获取本项目的相对路径,至关于<%=pageContext.request.ContextPath%>
。接下来咱们将表单<form>
标签中设置属性enctype="multipart/form-data"
。spring
Controller
层//保存用户 @RequestMapping(value = "/save", method = RequestMethod.POST) public String save(@RequestParam String username, @RequestParam String password, @RequestParam(value="image",required = false)MultipartFile image, HttpServletRequest request, Model model) { //获取文件在服务器上的储存位置 String path = request.getSession().getServletContext().getRealPath("resources/upload"); File filePath = new File(path); System.out.println("文件保存路径:" + path); if (!filePath.exists() && !filePath.isDirectory()) { System.out.println("目录不存在,建立目录:" + filePath); filePath.mkdir(); } //获取原始文件名称 String originalFileName = image.getOriginalFilename(); System.out.println("原始文件名称:" + originalFileName); //获取文件类型,以最后一个`.`做为标识 String type = originalFileName.substring(originalFileName.lastIndexOf(".") + 1); System.out.println("文件类型:" + type); //设置文件新名字 String fileName = System.currentTimeMillis() + "." + type; System.out.println("文件新名称:" + fileName); //在指定路径建立一个文件 File targetFile = new File(path, fileName); //将文件保存到服务器指定位置 try { image.transferTo(targetFile); model.addAttribute("message", "保存数据成功"); userService.save(username,password,"resources/upload/" + fileName); return "view/success"; } catch (IOException e) { System.out.println("保存文件错误..."); e.printStackTrace(); } return "view/error"; }
Controller层就比较复杂了,由于对于本案例而言文件上传操做的主要代码都在Controller层,而Service和Dao层仅是执行保存操做。咱们首先了解一下文件操做的API方法:数据库
- request.getSession().getServletContext().getRealPath(String s):获取本项目下指定目录的绝对路径。
- MultipartFile.getOriginalFilename():获取被
MultipartFile
绑定的上传参数的原始名称- System.currentTimeMillis():获取自1970年1月1日0时起到如今时间的毫秒数。
- MultipartFile.transferTo(): 在指定的磁盘路径下生成一个新的文件。
image
参数是MultipartFile
数据类型。request.getSession().getServletContext().getRealPath()
获取到的是本项目的绝对路径,而getRealPath("resources/upload")
是针对此项目的相对路径,如此时的文件保存路径实际上是:/Users/ty/Documents/Java/Learn/ssm/target/TyCoding/resources/upload
。mkdir()
方法建立此文件夹。save()
方法将表单数据保存到数据库中。(**注意:**在调用的save()
方法中咱们会看到实际写入进数据库中的上传文件的路径其实仅是相对此项目的相对路径(即相对于webapp
)。<!-- 配置支持文件上传 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:maxUploadSize="500000"/>
maxUploadSize
是设置上传文件的大小,能够更改。浏览器
下面咱们看一下实际效果: 点击提交按钮,访问后台映射方法:
能够看到已经获取到表中的数据,并准备调用Service层保存数据,那么咱们看一下最后的Dao层将数据存入进数据库的方法:tomcat
public class UserDaoImp extends JdbcDaoSupport implements UserDao { //保存数据 public void save(String username, String password, String image) { //使用Spring提供的JDBC模板能够直接执行SQL语句 this.getJdbcTemplate().update("insert into user(id,username,password,image) values(?,?,?,?)",null,username,password,image); } }
最后数据保存成功,跳转到成功页面:
看一下此文件在工程中的位置:
看一下储存到数据库中数据:
服务器
select * from user
,将查询到的数据回显到页面上: list.jsp
<table border="1"> <tr> <td>id</td> <td>username</td> <td>password</td> <td>avatar</td> </tr> <c:forEach items="${userList}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.password}</td> <td><img src="<%=basePath%>/${user.image}" width="100" height="80"></td> </tr> </c:forEach> </table>
答疑:
getRealPath("resources/upload")
不应是相对于工程的路径吗?(也就是在webapp
下)。为何上传的文件会保存到target
目录下? 答:咱们要明白Tomcat下运行的项目实际是编译后的class文件,即此maven项目中的target
文件夹中的数据。那么咱们设置的文件上传路径实际上是在此编译后的文件夹中的相对路径(TyCoding
是我设置的artifact id
)localhost:8080
下的路径,也就是当前项目的相对路径,因此咱们若是保存一个带盘符的绝对路径是确定不能访问到的。文件上传咱们这里设计的思路是:在页面设置一个可点击的链接(好比<a>
),点击便可下载,而咱们须要在其href
属性中拼接要下载的文件的名称,而后经过这个请求路径,Controller层的映射方法接收到你要下载的文件名称,而后根据指定的下载文件的路径查询到这个文件的名称,而后将文件转换成二进制流,而后让客户端读取这个二进制流写入到本机中从而实现下载。
ResponseEntity<byte[]>
: SpringMVC提供的用于实现响应头、文件数据(以字节储存)、状态封装都一块儿返回给浏览器实现文件的下载。header.setContentDispositionFormData()
: 告诉浏览器要以指定的数据类型打开这个文件FileUtils.readFileToByteArray()
: 使用FileUtils工具类强制将指定文件数据转换成byte字节流的形式。save.jsp
页面,新增一个下载的链接:<h2>文件下载</h2> <a href="<%=basePath%>/user/download?fileName=图片.jpg">点击我下载图片</a>
如上所示,咱们在请求路径中拼接了fileName
参数值是图片.jpg
那么后台接收到这个参数值,就会查询指定位置的文件。
//文件下载 @RequestMapping("/download") public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam(value="fileName",required = false) String fileName) throws Exception { try{ //下载路径 String path = request.getServletContext().getRealPath("/resources/upload/"); File file = new File(path + File.separator + fileName); HttpHeaders headers = new HttpHeaders(); //解决文件名中文乱码问题 String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1"); //告诉浏览器以"attachment"方式打开文件 headers.setContentDispositionFormData("attachment",downloadFileName); //设置请求头的媒体格式类型为 application/octet-stream(二进制流数据) headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers,HttpStatus.CREATED); } catch(Exception e){ e.printStackTrace(); System.out.println("文件下载出错..."); return null; } }
首先要指定映射方法的返回数据类型是ResponseEntity<byte[]>
,首先要规定一个下载文件的目录路径,须要下载文件时,就会中这个路径中查询须要下载的文件,若是找不到指定文件,那么就进catch里面。 File.separator
用来分隔同一个路径字符串中的目录,至关于/
。找到了指定路径下的文件后,须要解决中文乱码问题,而后告诉浏览器要以attachment
的方式打开这个文件。 最后这只ContentType
媒体格式类型,最后使用FileUtils
的readFileToByteArray
将文件数据转换成二进制字节流,连同设置好的响应数据格式一同返回给浏览器,实现文件的下载。
最后看一下实际的效果:
<br/>
若是你们有兴趣,欢迎你们加入个人Java交流群:671017003 ,一块儿交流学习Java技术。博主目前一直在自学JAVA中,技术有限,若是能够,会尽力给你们提供一些帮助,或是一些学习方法,固然群里的大佬都会积极给新手答疑的。因此,别犹豫,快来加入咱们吧!
<br/>
If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.