文件上传
对于文件上传,都不陌生,但是一写代码,博主感觉也很迷,因为文件上传的原理过程确实太复杂了,我们一般使用第三方提供的方法来实现。
鉴于原生的文件上传真的太难了,所以Apache提供了处理文件上传的jar包(按照以往的习惯,还是提供了Maven仓库的地址,需要者可以自己去下载)。
博主这里就直接使用Maven依赖了:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
首先,文件上传一定得需要表单页面吧,原生页面,别嫌弃low哈
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>文件上传</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/upload.do" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"/><br>
文件:<input type="file" name="fileName"/><br>
<input type="submit" value="提交">
</form>
</body>
</html>
上传文件的表单一定要注意,表单提交方式一定要是post
方式,而且一定要设置enctype="multipart/form-data"
,否则会失败。
提供处理上传文件的Servlet:
package cn.ara.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.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.UUID;
public class FileServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//判断上传的表单是普通表单还是文件表单
if (!ServletFileUpload.isMultipartContent(request)) {
return;//是一个普通的表单
}
try {
//创建上传文件的保存路径,直接放在WEB-INF路径下,安全,用户无法直接访问
String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
File uploadFile = new File(uploadPath);
if (!uploadFile.exists()) {
uploadFile.mkdir();//不存在该文件夹就创建
//打印绝对路径
System.out.println(uploadFile.getAbsolutePath());
}
//缓存 临时文件
//临时路径 如果文件超出预定的大小 就将它放到一个临时的文件中 一段时间后自动删除
String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
File tempFile = new File(tempPath);
if (!tempFile.exists()) {
tempFile.mkdir();//不存在该文件夹就创建
//打印绝对路径
System.out.println(tempFile.getAbsolutePath());
}
//处理上传的文件,一般都需要通过流来获取,可以使用request. getInputStream(),原生态的文件上:传流获取,十分麻烦
//我们一般都建议使用Apache的文件上传组件来实现,common-fileupload, 它需要依赖于commons- io组件
/*
在使用ServletFileUpload对象解析请求时需要DiskFileItemFactory对象。
所以,我们需要在进行解析工作前构造DiskFileItemFactory对象,
通过ServletFileUpload,对象的构造方法或setFileItemFactory()方法没置ServLetFileUpload对象的fileItemFactory属性。
*/
//1.创建DiskFileItemFactory对象,处理文件上: 传路径或者大小限制的;
DiskFileItemFactory factory = new DiskFileItemFactory();
//通过这个工厂设置一个缓冲区,当上:传的文件大于这个缓冲区的时候,将他放到临时文件中;
factory.setSizeThreshold(1024 * 1024); //缓存区大小为1M
factory.setRepository(tempFile);//临时目录的保存目录,需要一个File
//2.获取ServletFileUpload ServletFileUpload负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem对象
ServletFileUpload upload = new ServletFileUpload(factory);
//监听文件上传进度 可以参考实现文件上传时的进度条
upload.setProgressListener(new ProgressListener() {
@Override
//pBytesRead:已经读取到的文件大朴
//pContentLength :文件 大朴
public void update(long pBytesRead, long pContentLength, int pItems) {
//总大小 pContentLength
//已上传大小 pBytesRead
System.out.println("已上传: " + (pBytesRead*100) /pContentLength + "%");
}
});
//处理乱码问题
upload.setHeaderEncoding("UTF-8");
//1024=1kb*1024=1M*10=10M
//设置单个文件的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
//设置总共能够上传文件的大小
upload.setSizeMax(1024 * 1024 * 10);
//3.处理上传的文件
//解析前端请求,把表单中的每一个输入项封装成一个个的FileItem
List<FileItem> fileItems = upload.parseRequest(request);
//遍历单个FileItem
for (FileItem fileItem : fileItems) {
//判断输入项是一个普通输入项还是带文件的输入项
if (fileItem.isFormField()) {
//是一个普通表单
String name = fileItem.getFieldName();
String value = fileItem.getString("UTF-8");
System.out.println(name + ":" + value);
} else {
//是一个文件输入项
//============= 处理文件 开始 ===============
//获取上传的带路径的文件名
String uploadFileName = fileItem.getName();
//如果文件名为空或者文件名不合法 则舍弃这个输入项
if (uploadFileName == null || uploadFileName.trim().equals("")) {
continue;
}
//获得上传的文件名
String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1);
//获得文件的后缀名
String fileNameSuffix = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1);
System.out.println("文件信息[文件名:" + fileName + ", 文件类型:" + fileNameSuffix + "]");
//============= 处理文件 结束 ===============
//============= 存放地址 开始 ===============
//使用UUID保证文件夹名的唯一 一个文件对应一个UUID的文件夹
String uuidPath = UUID.randomUUID().toString();
String relPath = uploadPath + "/" + uuidPath;
File relPathFile = new File(relPath);
if (!relPathFile.exists()) {
relPathFile.mkdir();
}
//============= 存放地址 结束 ===============
//============= 文件传输 开始 ===============
//获取输入项中的流
InputStream inputStream = fileItem.getInputStream();
//创建一个输出流
FileOutputStream fileOutputStream = new FileOutputStream(relPath + "/" + fileName);
//缓冲区
byte[] buffer = new byte[1024 * 1024];
int len = 0;
while ((len = inputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, len);
}
//============= 文件传输 结束 ===============
//关闭流
fileOutputStream.close();
inputStream.close();
//删除临时文件
fileItem.delete();
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
//响应客户端
response.getWriter().write("OK!");
}
}
配置好Servlet之后,提交就可以发现上传的文件被存放到控制台打印的地址了。
文件上传就实现成功了。
文件上传有几个注意点:
- 为了保证上传文件的安全,上传的文件一般保存在外界无法直接访问的目录下。
- 为了防止文件的重复,应该为上传的文件提供一个唯一的文件名。
- 为了节省服务器的内存资源,上传的文件通常会限制大小。
- 为了上传文件的合法性,一般会通过后缀名限制文件上传的类型。
一般实际操作就视实际情况而定,博主这里没有限制文件上传的类型,仅供参考。