SpringMVC实现文件上传
1. 文件上传的必要前提
A form表单的enctype取值必须是:multipart/form-data (支持二进制数据)
(默认值是:application/x-www-form-urlencoded) (支持传递参数,例如:?
username=zhangsan&age=18)
enctype:是表单请求正文的类型
B method属性取值必须是Post
C 提供一个文件选择域<input type=”file” />
2. 文件上传的原理分析
当form表单的enctype取值不是默认值后(例如:multipart/form-data),request.getParameter()将失效。
默认值:
enctype=”application/x-www-form-urlencoded”时,form表单的正文内容是:
key=value&key=value&key=value
当form表单的enctype取值为Mutilpart/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和commons-io。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。
4. springmvc传统方式的文件上传
说明
传统方式的文件上传,指的是我们上传的文件和访问的应用存在于同一台服务器上。
并且上传完成之后,浏览器可能跳转。
实现步骤
第一步:创建项目,导入jar包
pom.xml
<!-- 版本锁定 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</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>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<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>
</dependencies>
第二步:web.xml
<!-- SpringMVC的核心控制器 -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置Servlet的初始化参数,读取springmvc的配置文件,创建spring容器 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet启动时加载对象 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置解决中文乱码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步:springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.itheima.controller"/>
<!--视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 设置静态资源不过滤 -->
<mvc:default-servlet-handler></mvc:default-servlet-handler>
</beans>
第四步:导入index.jsp
<body>
<a href="user/testFileUpload">文件上传</a>
</body>
第五步:导入UserController.java
@Controller
@RequestMapping(path = "/user")
public class UserController {
// 文件上传
@RequestMapping(path="/testFileUpload")
public String testFileUpload(){
System.out.println("执行了testFileUpload方法!");
return "success";
}
}
导入WEB-INF/pages/success.jsp
<body>
<h1>文件上传成功</h1>
</body>
发布到tomcat,测试。
5. 普通文件上传
index.jsp
<%--
enctype="multipart/form-data"把请求体分成多个部分上传
--%>
<form action="user/testFileUpload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br/>
<input type="submit" value="上传"/><br/>
</form>
UserController.java
// 文件上传
@RequestMapping(path="/testFileUpload1")
public String testFileUpload1(HttpServletRequest request) throws Exception {
System.out.println("执行了testFileUpload1方法!");
// 上传的位置,获取到项目根目录下的uploads文件夹绝对路径
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建file对象
File file = new File(path);
// 判断是否存在
if(!file.exists()) {
// 创建目录
file.mkdirs();
}
// 创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request
List<FileItem> list = fileUpload.parseRequest(request);
// 遍历,获取到每一个文件项的对象
for (FileItem fileItem : list) {
// 判断,当前fileItem是否是文件项
if(fileItem.isFormField()) {
// 说明是普通的表单 ,文本框
}else {
// 一定文件上传项 fileItem
// 获取到文件的名称
String filename = fileItem.getName();
// 生成唯一标识
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
filename = uuid+"_"+filename;
// 上传文件
fileItem.write(new File(file, filename));
// 删除临时文件
fileItem.delete();
}
}
return "success";
}
查看上传结果:
6. SpringMVC传统方式文件上传
简化ServletFileUpload的写法。
SpringMVC框架提供了MultipartFile对象,该对象表示上传的文件,要求变量名称必须和表单file标签的name属性名称相同。
第一步:springmvc.xml中配置
配置文件解析器对象
<!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--文件大小限制:10*1024*1024字节,表示10M-->
<property name="maxUploadSize" value="10485760"/>
</bean>
第二步:index.jsp
<hr>
<form action="user/testFileUpload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br/>
<input type="submit" value="上传"/><br/>
</form>
第三步:UserController.java
/**
* 使用的是SpringMVC的文件上传
* 框架提供了一个类,表示上传文件的对象,底层已经帮你解析了request对象,把文件上传对象通过方法的参数传递进来
*
* MultipartFile upload 变量名称必须和表单file标签的name属性名称相同,上传文件的对象,SpringMVC框架传递给你的。
*
* @param request
* @return
* @throws Exception
*/
@RequestMapping("/testFileUpload2")
public String testFileUpload2(HttpServletRequest request,MultipartFile upload) throws Exception {
System.out.println("SpringMVC的上传文件...");
// 上传的位置,获取到项目根目录下的uploads文件夹绝对路径
String path = request.getSession().getServletContext().getRealPath("/uploads");
// 创建file对象
File file = new File(path);
// 判断是否存在
if(!file.exists()) {
// 创建目录
file.mkdirs();
}
// 获取到文件的名称
String filename = upload.getOriginalFilename();
// 生成唯一标识
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
filename = uuid+"_"+filename;
// 使用upload对象中方法就直接上传文件
upload.transferTo(new File(file, filename));
return "success";
}
7. springmvc跨服务器方式的文件上传
1. 分服务器的目的
在实际开发中,我们会有很多处理不同功能的服务器。例如:
应用服务器:负责部署我们的应用 (源码)
数据库服务器:运行我们的数据库
缓存和消息服务器:负责处理大并发访问的缓存和消息
文件服务器:负责存储用户上传文件的服务器。
图片服务器:负责存储用户上传的图片的信息。
(注意:此处说的不是服务器集群)
分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。
准备两个tomcat服务器,并创建一个用于存放图片的web工程
第一步:创建工程
选择项目,右键,new一个Module
第二步:创建2个tomcat服务器
一个应用服务器:用来启动springmvc_day02_fileupload(端口:8080)
另一个图片服务器:用来启动springmvc_day02_fileuploadserver(端口:9090)
两个服务器的端口需要不一致
同时在图片服务器上创建upload文件夹,用来指定上传的文件。
导入jar包
在我们负责处理文件上传的项目中拷贝文件上传的必备jar包
导入开发需要的jar包
<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>
<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>
文件上传
<hr>
<form action="user/testFileUpload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br/>
<input type="submit" value="上传"/><br/>
</form>
UserController.java
/**
* 跨服务器上传文件,把文件上传到图片服务器中去
* @param upload
* @return
* @throws Exception
*/
@RequestMapping("/testFileUpload3")
public String testFileUpload3(MultipartFile upload) throws Exception {
System.out.println("跨服务器上传文件...");
// 指定上传文件的路径
String path = "http://localhost:9090/springmvc_day02_fileuploadserver/uploads/";
// 获取到文件的名称
String filename = upload.getOriginalFilename();
// 生成唯一标识
String uuid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
filename = uuid+"_"+filename;
// 上传文件
// 创建客户端对象
Client client = Client.create();
filename = path+filename;
// 连接图片服务器
WebResource webResource = client.resource(filename);
// 把文件上传到图片服务器上
webResource.put(upload.getBytes());
return "success";
}
查看上传的文件:
可在success.jsp中,显示上传的图片
<body>
${pageContext.request.contextPath}
<h1>访问成功!</h1>
<img src="http://localhost:8088/springmvc_day02_fileuploadserver/upload/D7E35E6EDAC94039BDE6533BEFF8F981_02.jpg"/>
</body>
注意:如果抛出异常:returned a response status of 403 Forbidden
出现这个错误的原因是因为我将图片上传单独部署了一个服务器。但是图片不能写入。需要在这个存储图片的项目所在的tomcat中配置可写操作。具体的是在Tomcat目录下的conf文件夹下的web.xml中加入
加入此行的含义是:接收文件的目标服务器可以支持写入操作。
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>