本篇Blog继续拓展了解SpringMVC框架是如何实现文件上传和下载的。文件上传和下载是项目开发中最常见的功能。我们来实践一下,当然提前准备好实践环境:
并且在其中导入相关的Maven依赖,这里包含commons-fileupload
:
全量依赖如下:
<dependencies> <!-- commons-fileupload --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <!-- jackson包引入--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency> <!--Spring MVC框架依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <!--JSP相关依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!--servlet相关依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--单元测试相关依赖--> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies>
接着是我们的传统艺能,注册SpringMVC过滤器来拦截请求
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置前端控制器--> <servlet> <!--1.注册DispatcherServlet--> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联--> <init-param> <param-name>contextConfigLocation</param-name> <!--关联一个springMVC的配置文件:【servlet-name】-servlet.xml--> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!--启动级别-1,在Tomcat启动时就初始化Spring容器--> <load-on-startup>1</load-on-startup> </servlet> <!--/ 匹配所有的请求;(不包括.jsp)--> <!--/* 匹配所有的请求;(包括.jsp)--> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
文件上传
文件上传是项目开发中最常见的功能之一 ,SpringMVC 可以很好的支持文件上传。为了能上传文件,表单提交和SpringMVC框架必须满足如下两个条件:
- 表单提交的method需要设置为POST,并将enctype设置为multipart/form-data。一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。
- SpringMVC的文件上传需要依赖Apache Commons FileUpload的组件,Spring MVC 为文件上传提供了直接的支持,这种支持是用即插即用的 MultipartResolver实现的。Spring MVC 使用
Apache Commons FileUpload
技术实现了一个MultipartResolver
实现类:CommonsMultipartResolver
这里的enctype设置一般有如下几个选项:
- application/x-www=form-urlencoded:默认方式,只处理表单域中的 value 属性值,采用这种编码方式的表单会将表单域中的值处理成 URL 编码方式。
- multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
- text/plain:除了把空格转换为 “+” 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
接下来就实践下文件上传和下载
1 注册multipartResolver解析器
首先我们需要在Bean注册文件上传的解析器,这样SpringMVC框架才能直接使用
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --> <context:component-scan base-package="com.example.file.controller"/> <!-- 让Spring MVC不处理静态资源 --> <mvc:default-servlet-handler /> <!-- 支持mvc注解驱动 在spring中一般采用@RequestMapping注解来完成映射关系 要想使@RequestMapping注解生效 必须向上下文中注册DefaultAnnotationHandlerMapping 和一个AnnotationMethodHandlerAdapter实例 这两个实例分别在类级别和方法级别处理。 而annotation-driven配置帮助我们自动完成上述两个实例的注入。 --> <mvc:annotation-driven /> <!--视图解析器--> <!--通过视图解析器解析处理器返回的逻辑视图名并传递给DispatcherServlet--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="UTF-8"/> </bean> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="failOnEmptyBeans" value="false"/> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- 配置文件上传的解析器:--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 上传文件大小上限,单位为字节(10MB) --> <property name="maxInMemorySize"> <value>10485760</value> </property> <!-- 设定默认编码 等 --> <property name="defaultEncoding" value="UTF-8"></property> </bean> </beans>
2 编写前端JSP页面
前端我们需要编写一个上传的页面,用来上传文件
<%-- Created by IntelliJ IDEA. User: 13304 Date: 2021/9/12 Time: 22:17 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="file/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form> </body> </html>
3 编写后端Controller
我们主要使用CommonsMultipartFile对象来处理文件对象,进行相关的操作。MultipartFile 封装了请求数据中的文件,此时这个文件存储在内存中或临时的磁盘文件中,需要将其转存到一个合适的位置,因为请求结束后临时存储将被清空
package com.example.file.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.commons.CommonsMultipartFile; import javax.servlet.http.HttpServletRequest; import java.io.*; @RestController @RequestMapping("/file") public class FileController { //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象 @RequestMapping("/upload") public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException { // 判断文件是否为空,空则返回失败页面 if (file.isEmpty()) { return "文件为空,无法上传"; } // 获取文件存储路径(绝对路径) String path = request.getServletContext().getRealPath("/WEB-INF/file"); // 获取原文件名 String fileName = file.getOriginalFilename(); // 创建文件实例 File filePath = new File(path, fileName); // 如果文件目录不存在,创建目录 if (!filePath.getParentFile().exists()) { filePath.getParentFile().mkdirs(); System.out.println("创建目录" + filePath); } // 写入文件 file.transferTo(filePath); return "文件上传成功"; } }
4 文件上传测试
我们上传文件进行测试,在jsp页面上上传文件:
然后在服务器目录上就可以看到该文件:
文件下载
文件下载比较简单,SpringMVC提供了一个 ResponseEntity 类型,使用它可以很方便地定义返回的 HttpHeaders 和HttpStatus
1 编写前端JSP页面
前端我们需要编写一个JSP页面用来输入要下载的文件名并且下载
<%-- Created by IntelliJ IDEA. User: tianmaolin Date: 2021/9/13 Time: 8:35 下午 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <form action="file/download" enctype="multipart/form-data" method="post"> <input type="text" name="fileName"/> <input type="submit" value="download"> </form> </body> </html>
2 编写后端Controller
后端我们通常使用ResponseEntity对象来处理返回的文件:
@RequestMapping(value="/download") public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam("fileName") String fileName) throws Exception{ //下载文件路径 String path = request.getServletContext().getRealPath("/WEB-INF/file"); File filePath = new File(path, fileName); //设置请求头 HttpHeaders headers = new HttpHeaders(); //下载显示的文件名,并解决中文名称乱码问题 String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1"); //通知浏览器以attachment(下载方式)打开 headers.setContentDispositionFormData("attachment", downloadFileName); //applicatin/octet-stream: 二进制流数据(最常见的文件下载) headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(filePath), headers, HttpStatus.CREATED); }
3 文件下载测试
然后我们在浏览器中请求文件下载地址,并输入我们刚上传的文件名进行下载:
总结一下
文件上传和下载都还是比较简单的,但是如果不借助SpringMVC,而是通过Servlet去实现则还需要处理复杂的字节流或者字符流,有了框架以及框架提供给我们的对象,可以方便的进行各种操作,例如转存文件,原样返回给浏览器服务器文件。学到这里SSM框架就学习完了,相当于打好了基础,接下来就是上主菜Spring Boot了。