SpringMVC实现文件上传
一、文件上传的回顾
1). 文件上传的必要前提
- form表单的
enctype
取值必须是multipart/form-data
,(默认值是:application/x-www-form-urlencoded
);enctype:
请求头表示的是请求正文的类型。 - method属性取值必须是
post
- 需要提供一个文件选择域
<input type="file" />
2). 文件上传原理分析
当
enctype="application/x-www-form-url"
及默认值,form表单的正文内容为key=value&key=value...
,当form表单的enctype的取值不是默认值后,request.getParameter()
将失效.
当form表单的enctype取值为
Multipart/form-data
时,请求正文内容就变成:每一部分都是MIME类型描述的正文。
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 协议头
aaa 协议的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 协议的类型(MIME 类型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 二进制数据
-----------------------------7de1a433602ac--
3). 借助第三方组件实现文件上传
使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:
Commons-fileupload
(1.3.1)和commons-io
(2.4)。commons-io不属于文件上传组件的开发jar文件,但Commons-fileupload组件从1.1版本开始,它工作时需要commons-io包的支持。
二、传统方式上传文件
1). 导入第三方maven坐标
<!-- 文件上传第三方包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
2). 上传文件页面index.jsp
<h1>传统方式实现文件上传</h1>
<form action="/user/fileupload1" method="post" enctype="multipart/form-data">
选择文件: <input type="file" name="upload" /> <br>
<input type="submit" value="上传">
</form>
3). 文件上传控制器
@Controller
@RequestMapping("/user")
public class UserController {
/** 传统的文件上传方式 */
@RequestMapping("/fileupload1")
public String fileUpload1(HttpServletRequest request) throws Exception {
System.out.println("文件上传...");
// 使用fileipload组件完成文件上传
// 上传的位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判断该路径是否存在
File file = new File(path);
if(!file.exists()) {
file.mkdirs();
}
// 解析request对象,获取上传文件项
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
// 遍历
for (FileItem item : items) {
// 进行判断,当前item对象是否是上传文件项
if(item.isFormField()) {
// 说明普通表单项
} else {
// 说明是上传文件项
// 获取上传文件的名称
String filename = item.getName();
// 将文件名设置成唯一值uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上传
item.write(new File(path, filename));
// 删除上传文件请求的临时文件
item.delete();
}
}
return "success";
}
}
4). 返回界面success.jsp
<h1>操作成功!</h1>
三、SpringMVC方式的文件上传
传统方式的文件上传,同时指的是我们上传的文件和访问的应用存在于同一台服务器上。并且上传完成之后,浏览器可能跳转。
1). 导入maven工程坐标
<dependencies>
<!-- 文件上传第三方包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<!-- spring包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- jackson包等 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 其他包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
2). 配置文件解析器 springMVC.xml
配置文件上传解析器,解析器的id是固定的,不能起别的名字,否则无法实现请求参数的绑定。不光是文件,其他字段也将无法绑定;同时可以配置一些属性
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>524288</value>
</property>
</bean>
3). 编写jsp页面
<%-- 文件上传 --%>
<form action="${pageContext.request.contextPath}/file/fileUpload"
method="post" enctype="multipart/form-data">
名称: <input type="text" name="picname"> <br>
图片: <input type="file" name="uploadFile"> <br>
<input type="submit" value="上传">
</form>
4). 编写控制器代码
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
@Controller
@RequestMapping("/file")
public class FileController {
/** 文件上传的控制器,MultipartFile后面的
形参必须和前端传送的文件Input的name一致 */
@RequestMapping("/fileUpload")
public String testResponseJson(String picname,
MultipartFile uploadFile,
HttpServletRequest request) throws Exception {
// 定义文件名
String fileName = "";
// 1. 获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
// 2. 截取文件扩展名
String extendName = uploadFileName.substring(
uploadFileName.lastIndexOf(".") + 1
);
// 3. 把文件加上随机数,方式文件重复
String uuid = UUID.randomUUID().toString()
.replace("-", "")
.toUpperCase();
// 4. 判断是否输入了文件名
if(!StringUtils.isEmpty(picname)) {
fileName = uuid + "_" + picname + "." + extendName;
} else {
fileName = uuid + "_" + uploadFileName;
}
System.out.println(fileName);
// 获取文件路径
ServletContext context = request.getServletContext();
String basePath = context.getRealPath("/uploads");
// 解决同一文件夹中文件过多问题
String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
// 判断路径是否存在: 发布的路径,并不是工作空间的路径!
String realPath = basePath + "/" + datePath;
System.out.println("真实路径:" + realPath);
File file = new File(realPath);
if(!file.exists()) {
file.mkdirs();
}
// 使用MultipartFile接口中的方法,把上传的文件写到指定位置
uploadFile.transferTo(new File(file, fileName));
return "success";
}
}
四、SpringMVC跨服务器方式的文件上传
1). 分服务器的目的
在实际开发中,我们会有很多处理不同功能的服务器。分服务器处理的目的是为了让服务器各司其职,从而提高项目运行的效率【这里不是指服务器集群】
服务器 | 功能 |
---|---|
应用服务器 | 负责部署应用 |
数据库服务器 | 运行数据库 |
缓存和消息服务器 | 负责处理大并发访问的缓存和消息 |
文件服务器 | 负责存储用户上传文件的服务器 |
2). maven坐标
<!-- ...其他坐标同上.. -->
<!-- sun公司提供的 客户端 坐标 -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
3). 准备两个tomcat服务器
一个服务器用于跑应用,另一个服务器负责上传文件,首先将本地安装的tomcat服务器配置成允许上传。在conf/web.xml中加一点配置,允许上传。
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
1. 配置存放文件的tomcat服务器。
- 新建一个maven的web工程
SpringMVC_01_fileUpload
,在target/SpringMVC_01_fileUpload/
下新建一个文件夹/uploads
,否则会报409错误 - 添加一个本地的tomcat服务器,命名为fileUpload,指定端口为81,JMX port为1100,项目访问虚拟路径为
/fileUpload
,添加项目发布包SpringMVC_01_fileUpload:war exploded
- 启动项目
2). 应用服务器描述
使用的端口为80,JMX port为1100,... ,下面介绍的为应用服务器的配置+关键代码描述。
- 配置文件解析器springMVC.xml
id值是底层规定好了的,不能随意修改,否则spring无法识别;可以配置一些属性
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>524288</value>
</property>
</bean>
- index.jsp代码
<%-- 文件上传 --%>
<form action="${pageContext.request.contextPath}/file/fileUpload"
method="post" enctype="multipart/form-data">
名称: <input type="text" name="picname"> <br>
图片: <input type="file" name="uploadFile"> <br>
<input type="submit" value="上传">
</form>
- 控制器代码
import java.util.UUID;
@Controller
@RequestMapping("/file")
public class FileController {
public static final String FILESERVERURL = "http://localhost:81/fileUpload/uploads/";
/** 文件上传的控制器
MultipartFile 后面的形参必须和前端对应的name属性一致
*/
@RequestMapping("/fileUpload")
public String testResponseJson(String picname, MultipartFile uploadFile) throws Exception {
// 定义文件名
String fileName = "";
// 1. 获取原始文件名
String uploadFileName = uploadFile.getOriginalFilename();
// 2. 截取文件扩展名
String extendName = uploadFileName.substring(
uploadFileName.lastIndexOf(".") + 1
);
// 3. 把文件加上随机数,防止文件重复
String uuid = UUID.randomUUID().toString()
.replace("-", "")
.toUpperCase();
// 4. 判断是否输入了文件名
if(!StringUtils.isEmpty(picname)) {
fileName = uuid + "_" + picname + "." + extendName;
} else {
fileName = uuid + "_" + uploadFileName;
}
System.out.println(fileName);
// 创建sun公司提供的jersey包中的Client对象
Client client = Client.create();
// 指定上传文件的地址,该地址是web路径
WebResource resource = client.resource(FILESERVERURL + fileName);
// 实现上传
String result = resource.put(String.class, uploadFile.getBytes());
System.out.println(result);
return "success";
}
}