文章目录
- 1. 文件上传的回顾
- 1.1 文件上传的前提
- 1.2 文件上传的原理分析
- 1.3 借助第三方组件实现文件上传
- 2. SpringMVC 传统方式的文件上传
- 2.1 Spring MVC 文件上传原理分析:
- 3. Spring MVC 跨服务器方式的文件上传
1. 文件上传的回顾
1.1 文件上传的前提
(1)form 表单的 enctype 的取值必须是 multipart/form-data
,
(默认值是 appplication/x-www-form-urlencoded
)
enctype 是表单请求正文的类型。
(2)method 属性取值必须是 Post
(3)提供一个文件选择域:<input type=:“file”/>
1.2 文件上传的原理分析
当 form 表单的 enctype 取值不是默认值后,request.getParameter() 将失败,取默认值时,即enctype=“application/x-www-form-urlencoded”,form表单的正文内容是:key=value&key=value,是以键值对 key value 的形式,而当 form 表单的 enctype 取值为 multipart/form-data 时,请求正文内容就变成:
每一部分都是 MIME 类型描述的正文,依次是:
- 分界符
- 协议头
- 协议的正文
- 协议的类型(MIME 类型)
接下来我们可以逐步解析,获取文件内容,通过输出流写入到指定文件。
1.3 借助第三方组件实现文件上传
使用 Commons-fileupload 组件实现文件上传,需要导入该组件相应的支撑 jar 包:Commons-fileupload 和 commos-io。commos-io 不属于 文件上传组件的开发 jar 包,但 Commons-fileupload 需要 commos-io 包的支持。
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
2. SpringMVC 传统方式的文件上传
“传统方式的文件上传”,指的是 上传的文件和访问的应用在同一台服务器上,并且上传完成之后,浏览器可能跳转。
新建一个 project,选择骨架 webapp,需要导入依赖、提供 SpringMVC.xml、在 web.xml 中提供前端控制器 和 中文过滤器、重建 index.jsp 、新建 UserController 、新建 WEB-INF 下的 pages 的 success.jsp 、部署 Tomcat 与上一篇的相同。
UserController 类:
@Controller
@RequestMapping("/user")
public class UserController {
//文件上传
@RequestMapping("/fileupload1")
public String fileUpLoad1(HttpServletRequest request) throws Exception {
System.out.println("文件上传");
//使用组件完成文件上传
//指定上传的位置
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);
List<FileItem> items=upload.parseRequest(request);
//遍历
for(FileItem item:items)
{
//判断当前 item 是否是上传文件项,
// 如果是,需要把文件存在路径下,否则需要额外处理
if(item.isFormField())
{
//说明是普通表单项
}
else
{
//需要获取到上传文件的名称
String fileName=item.getName();
//把文件的名称设置为唯一值
String uid=UUID.randomUUID().toString().replace("-","");
fileName=uid+"_"+fileName;
//需要获取到上传文件的名称
String fileName=item.getFieldName();
//完成文件上传
item.write(new File(path,fileName));
//删除临时文件
item.delete();
}
}
return "success";
}
}
<h3>文件上传</h3>
<form action="user/fileupload1" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br>
<input type="submit" valie="上传"/>
</form>
如果 Tomcat 的 Deployment 设置成 war,就会上传到 Tomcat 中:
如果设置成 war:exploded ,就会上传到 这个类的目录中:
文件名加了随机ID:
2.1 Spring MVC 文件上传原理分析:
配置一个文件解析器组件,那么前端控制器就会调用这个文件解析器(CommonsMultipartResolver 类,id 必须是multipartResoilver ),解析 request,返回 upload上传对象 给前端控制器,而 Controller 中需要提供方法通过参数绑定的方式,方法传入的参数是 MultipartFile,这个参数就是 文件上传项。这样就不用手动解析、遍历了。
<h3>SpringMVC 文件上传</h3>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br>
<input type="submit" valie="上传"/>
</form>
配置解析器:上传文件的大小最大是10兆
<!--配置文件解析器-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxInMemorySize" value="10485760"/>
控制类中:
@RequestMapping("/fileupload2")
public String fileUpLoad2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("文件上传");
//使用组件完成文件上传
//指定上传的位置
String path=request.getSession().getServletContext().getRealPath("/uploads/");
//判断文件是否存在
File file=new File(path);
//如果存在
if(!file.exists())
{
//创建文件夹
file.mkdirs();
}
//需要获取到上传文件的名称
String fileName=upload.getOriginalFilename();
/* String fileName=item.getName();*/
//把文件的名称设置为唯一值
String uid=UUID.randomUUID().toString().replace("-","");
fileName=uid+"_"+fileName;
/*完成文件上传
item.write(new File(path,fileName));
删除临时文件
item.delete();*/
//完成文件上传
upload.transferTo(new File(path,fileName));
return "success";
}
3. Spring MVC 跨服务器方式的文件上传
分服务器的目的:
在实际开发中,我们会有很多处理不同功能的服务器:
(1)应用服务器:负责部署我们的应用
(2)数据库服务器:运行我们的数据库
(3)缓存和消息服务器:负责处理大并发访问的缓存和消息
(4)文件服务器:负责储存用户上传文件的服务器
(注意:这里说的不是服务器集群)
向应用服务器发送请求,再需要搭建一个图片服务器。
先导入依赖:
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-client -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-core -->
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
先搭建图片服务器,需要部署 Tomcat:
在这个模块的 index.jsp 中写:
<html>
<body>
<h2>Hello FileUpload Server</h2>
</body>
</html>
在 webapp 下新建 uploads 文件夹。
把这个模块部署到 Tomcat 中:
<h3>跨服务器文件上传</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="upload"><br>
<input type="submit" valie="上传"/>
</form>
//跨服务器上传
@RequestMapping("/fileupload3")
public String fileUpLoad3(MultipartFile upload) throws Exception {
System.out.println("跨服务器文件上传");
//定义上传服务器的路径
String path="http://localhost:9090/uploads/";
//获取上传文件的名称
String fileName=upload.getOriginalFilename();
//把文件的名称设置成唯一值
String uid=UUID.randomUUID().toString().replace("-","");
fileName=uid+"_"+fileName;
//创建客户端对象
Client client=Client.create();
//连接图片服务器
WebResource resource=client.resource(path+fileName);
//上传文件
resource.put(upload.getBytes());
return "success";}
这时报错了 500:
而且在该类的路径下没有看到 uploads 文件夹:
需要手动创建一个 uploads 文件夹。
然鹅还是报错了:
在 Tomcat 的 conf 包下的 web.xml 中添加 readonly:false 即可:
分别 run 两个 Tomcat,就可以看到 跨服务器上传成功的结果: