JavaWeb学习(十二)—如何上传文件(附注释超详细的源码)
01 源码展示
Servlet
源码:
准备工作:需要导入common-io
和common-fileupload
组件
package com.hooi.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.UUID;
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/*
1. 通过ServletFileUpload判断用户的请求是普通表单还是带文件的表单
*/
if (!ServletFileUpload.isMultipartContent(req)){
//如果是普通表单,直接返回
return;
//如果是带有文件的表单,执行步骤2
}
/*
2. 创建文件的保存路径,建议保存在WEB-INF路径下,比较安全,用户无法直接访问该路径下的文件
*/
//2.1 创建文件的真实保存路径
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()){
//如果目录不存在,创建这个目录
uploadFile.mkdir();
}
//2.2 创建临时路径,如果文件超过了预期的大小,将它先存放至临时路径下,完成上传后,删除临时文件。
String tmpPath = this.getServletContext().getRealPath("/WEB-INF/tmp");
File tmpfile = new File(tmpPath);
if (!tmpfile.exists()){
//如果目录不存在,创建这个目录
tmpfile.mkdir();
}
//===========================至此,完成路径的创建===========================
/*
3.使用DiskFileItemFactory加工临时文件的保存目录:
如:做处理文件上传路径和限制文件大小的加工
一般来说,可以通过流来获取,
比如request.getInputStream(),
但是,采用原生态的流获取文件十分麻烦
因此,我们使用Apache的文件上传组件common-fileupload来实现,
这个组件依赖于common-io组件。
*/
//3.1.创建DiskFileItemFactory对象
DiskFileItemFactory factory = new DiskFileItemFactory();
//3.2 通过工厂设置缓冲区,上传文件大于缓冲区大小时,将该文件先存至临时文件中
factory.setSizeThreshold(1024*1024);//缓存区大小为1M
factory.setRepository(tmpfile);//设置临时文件的保存目录,需要传入临时文件的目录
//========================至此,完成文件目录的加工=========================
/*
4.使用ServletFileUpload处理上传文件
ServletFileUpload对象负责处理上传的文件,并将文件封装为FileItem对象.
在使用ServletFileUpload对象解析请求时需要传入DiskFileItemFactory对象。
*/
//4.1 获取ServletFileUpload对象
// (需要传入工厂对象(步骤3中经过处理的文件目录))
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//4.2 监听文件的上传进度
fileUpload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已经读取到的文件的大小
//pContentLength:文件大小
public void update(long pBytesRead, long pContentLength, int pItem) {
System.out.println("文件总大小"+pContentLength+"已上传"+pBytesRead);
}
});
//4.3 处理乱码问题
fileUpload.setHeaderEncoding("UTF-8");
//4.4 设置上传单个文件的最大值,单位bit
fileUpload.setFileSizeMax(1024*1024*10);
//4.5 设置上传多个文件的最大值(总共可以上传的文件的大小)
fileUpload.setSizeMax(1024*1024*100);
//4.6 解析前端请求,并将其封装为FileItem对象
/*
FileItem对象: 使用ServletFileUpload对象的parseRequest(Request)方法
可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem对象,
然后以List列表的形式返回。
*/
try {
List<FileItem> fileItems = fileUpload.parseRequest(req);
for (FileItem fileItem : fileItems) {
//带文件的表单也存在不是文件的表单项,比如本案例中的username,是text属性
//isFormField();判断上传的文件是普通的表单还是带文件的表单
if (fileItem.isFormField()){
//getFieldName方法用于返回表单标签name属性的值。
String name = fileItem.getFieldName();
//getString方法用于将FileItem对象中保存的数据流内容以一个字符串返回
String value = fileItem.getString("UTF-8");
System.out.println(name+":"+value);
} else{
//getName方法用于获得文件上传字段中的文件名。
String uploadFileName = fileItem.getName();
System.out.println(""+uploadFileName);
if (uploadFileName.trim().equals("")||uploadFileName==null){
continue;//文件名不存在或为空字符串,无法保存
}
//获得上传的文件名 假设选择上传文件时文件的路径为/xxx/xxx/xxx.jpg
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
//获得文件的后缀名
/*
获取文件后缀名的应用:
如果文件后缀名不是网站所要求的文件类型,可以直接return,
然后通知用户文件类型不正确
*/
String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".")+1);
System.out.println("上传的文件为:"+fileName+" 文件类型:"+fileExtName);
/*
为了保证文件路径名唯一使用UUID(唯一识别通用码),还可以使用日期区分
UUID.randomUUID(); 随机产生一个唯一识别的通用码
*/
//创建文件的路径名
String uuidPathName = UUID.randomUUID().toString();
//====================至此,完成文件处理=====================
/*
5. 为每个文件创建存储目录
*/
//给每个文件创建一个对应的目录,
//文件将存储在uploadPath目录下的uuidPathName目录下
String realPath = uploadPath+"/"+uuidPathName;
File realPathFile = new File(realPath);
if (!realPathFile.exists()){
realPathFile.mkdir();
}
//=======================至此,完成文件的保存===================
/*
6. 传输文件内容
*/
//6.1 获得上传文件的流(读取文件内容)
InputStream inputStream = fileItem.getInputStream();
//6.2 创建文件输出流(写入文件内容)
FileOutputStream outputStream = new FileOutputStream(realPath+"/"+fileName);
//6.3 创建一个缓冲区
byte[] buffer = new byte[1024*8];
//6.4 判断是否读取完毕,len大于0说明还有数据没有传输完毕
int len = 0;
if ((len=inputStream.read(buffer))>0){
outputStream.write(buffer,0,len);
}
//6.5 关闭流
inputStream.close();
outputStream.close();
//6.6 清除临时文件
fileItem.delete();
//=======================至此,完成文件的传输===================
/*
7. 返回上传结果
*/
req.setAttribute("msg","上传成功!");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
req.setAttribute("msg","上传失败!");
req.getRequestDispatcher("info.jsp").forward(req,resp);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
req.setAttribute("msg","上传失败!");
req.getRequestDispatcher("info.jsp").forward(req,resp);
} catch (IOException e) {
e.printStackTrace();
req.setAttribute("msg","上传失败!");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
}
}
upload.jsp
源码:
<body>
<%--
上传文件需要注意的事项
1.method必须为post,get无法获取大数据
2.enctype="multipart/form-data 表示表单中存在数据上传
--%>
<div>
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>选择文件:<input type="file" name="file"></p>
<p><input type="submit" value="上传文件"></p>
</form>
</div>
</body>
</html>
info.jsp
源码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>提示信息</title>
</head>
<body>
${pageContext.request.getAttribute("msg")}
</body>
</html>
web.xml
源码:
<?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"
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>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.hooi.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/upload.do</url-pattern>
</servlet-mapping>
</web-app>
02 结果展示
上传文件页面:
响应消息:
控制台打印信息:
03 错误总结
错误1: 在upload.jsp中,忘记键入表单的提交方式method=post
,导致在servlet的处理中,直接进入了非文件表单的识别逻辑(即,步骤1)。
错误2: 在获取文件名的代码中,在lastIndexOf()方法中错误的将传入的参数写为了".+1"
,导致无法获取文件名。
错误3: 在传输文件的过程中,将传入输出流的参数写错,把file对象当文件路径进行了字符串的拼接,导致找不到文件目录的存在。