本篇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了。