JavaWeb文件上传
在Web应用中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传功能的实现。
1. 准备
对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的。
一般选择采用apache的开源工具common-fileupload
这个文件上传组件。
common-fileupload
是依赖于common-io
这个包的,所以还需要下载这个包。
jar包的下载地址为:
https://mvnrepository.com/artifact/commons-io/commons-io
https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload
将 jar包导入到web项目的lib
目录下。
2. 使用类介绍
2.1 ServletFileUpload 类
ServletFileUpload
负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem
对象, 在使用ServletFileUpload
对象解析请求时需要DiskFileItemFactory
对象。所以,我们需要在进行解析工作前构造好DiskFileItemFactory
对象,通过ServletFileUpload
对象的构造方法或setFileItemFactory()
方法设置ServletFileUpload
对象的fileItemFactory
属性。
ServletFileUpload
负责处理上传的文件数据,并将表单中每个输入项封装成一个FileItem
对象中 . 使用其parseRequest(HttpServletRequest)
方法可以将通过表单中每一个HTML标签提交的数据封装成一个FileItem
对象,然后以List列表的形式返回。使用该方法处理上传文件简单易用。
2.2 FileItem类:
在HTML页面 input 必须有 name, <input type="file" name="filename">
表单如果包含一个文件上传输入项的话,这个表单的enctype
属性就必须设置为multipart/form-data
<form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br/>
上传文件1:<input type="file" name="file1"><br/>
上传文件2:<input type="file" name="file2"><br/>
<input type="submit" value="提交">
</form>
浏览器表单的类型如果为multipart/form-data
, 在服务器端想获取数据就要通过流
-
FileItem类的常用方法:
FileItem常用方法 功能 boolean isFormField(); 用于判断FileItem类对象封装的数据是普通文本表单,还是文件表单。
如果是普通表单字段则返回true,否则返回falseString getFieldName(); 返回表单标签name属性的值 String getString(); 将FileItem对象中保存的数据流内容以一个字符串返回 String getName(); 获得文件上传字段中的文件名。 InputStream getInputStream(); 以流的形式返回上传文件的数据内容。 void delete(); 用来清空FileItem类对象中存放的主体内容
如果主体内容被保存在临时文件中,delete方法将删除该临时文件。
3. 代码实现
项目的目录为:
3.1 代码实现的思路:
- Step1:判断表单类型。若是普通表单,return;若是文件表单,跳转到step2。
- Step2:创建上传文件的保存路径。若文件超过预期大小,则创建临时路径。
- Step3:处理上传文件。(使用 Apache的文件上传组件来实现)
- 创建
DiskFileItemFactory
对象 - 获取
ServletFileUpload
- 处理上传文件
- 把前端请求解析封装成一个
FileItem
对象,进行文件处理
- 把前端请求解析封装成一个
- 创建
3.2 代码
-
文件上传实现,
UploadServlet.java
package org.javaweb.upload; 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 UploadServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException { try { //1.先判断是否为普通表单,还是带文件的表单 if(!ServletFileUpload.isMultipartContent(request)){ //如果为普通,直接返回 return; } //如果通过了这个if,是带文件的表单 //2.创建上传文件的保存路径,建议在WEB-INF路径下,安全,用户无法直接访问上传的文件 String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload"); File uploadFile = new File(uploadPath); if(!uploadFile.exists()){ //如果目录不存在,则创建 uploadFile.mkdir(); } //3.临时路径,文件超过预期大小时,放在一个临时文件中 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); File file = new File(tempPath); if(!file.exists()){ //如果目录不存在,则创建 file.mkdir(); } //===============准备完成==============// //4、处理上传文件,一般需要通过流来获取,使用request.getInputStream(),原生态的文件上传流获取,十分麻烦 //建议使用Apache的文件上传组件实现,commmon-fileupload,它依赖于common-io组件 //4.1创建DisFileItemFactory对象,处理文件上传路径或者大小限制的 DiskFileItemFactory factory = getDiskFileItemFactory(file); //4.2 获取ServletFileUpload ServletFileUpload upload = getServletFileUpload(factory); //4.3 处理上传的文件 String msg = uploadParseRequest(upload, request, uploadPath); //servlet请求转发消息 request.setAttribute("msg",msg); request.getRequestDispatcher("info.jsp").forward(request,response); } catch (FileUploadException e) { e.printStackTrace(); } } //4.1创建DisFileItemFactory对象,处理文件上传路径或者大小限制的 public static DiskFileItemFactory getDiskFileItemFactory(File file){ DiskFileItemFactory factory = new DiskFileItemFactory(); //通过这个工厂设置一个缓冲区,当上传的文件大于这个缓冲区的时候,将他放到临时文件中; factory.setSizeThreshold(1024*1024); //缓存区大小为1M factory.setRepository(file); //临时目录的保存目录,需要一个File return factory; } //4.2 获取ServletFileUpload public static ServletFileUpload getServletFileUpload(DiskFileItemFactory factory){ ServletFileUpload upload = new ServletFileUpload(factory); //监听文件上传速度 upload.setProgressListener(new ProgressListener() { @Override //pBytesRead:已经读取到的文件大小 //pContentLength : 文件大小 public void update(long pBytesRead, long pContentLength, int pItems) { System.out.println("总大小:" + pContentLength + "已上传:" + pBytesRead); } }); //处理乱码问题 upload.setHeaderEncoding("UTF-8"); //设置单个文件的最大值 upload.setFileSizeMax(1024 * 1024 * 10); //设置总共能够上传文件的大小 //1024 = 1kb * 1024 = 1M * 10 = 10M upload.setSizeMax(1024 * 1024 * 10); return upload; } //4.3 处理上传的文件 public static String uploadParseRequest(ServletFileUpload upload,HttpServletRequest request,String uploadPath) throws FileUploadException, IOException { String msg = ""; //3.把前端请求解析,封装成一个FileItem对象 List<FileItem> fileItems = upload.parseRequest(request); for (FileItem fileItem : fileItems) { if (fileItem.isFormField()){ //判断上传的文件是普通的表单还是带文件的表单 //getFieldName指的是前端表单控件的name; String name = fileItem.getFieldName(); String value = fileItem.getString("UTF-8"); //处理乱码 System.out.println(name+":"+value); }else { //判断它是上传的文件 //===============处理文件==================// //拿到文件名字 String uploadFileName = fileItem.getName(); System.out.println("上传的文件名:"+uploadFileName); if (uploadFileName.trim().equals("")||uploadFileName==null){ continue; } //获得上传的文件名 /images/girl/paojie.png String fileName = uploadFileName.substring(uploadFileName.lastIndexOf("/") + 1); //获得文件的后缀名 String fileExtName = uploadFileName.substring(uploadFileName.lastIndexOf(".") + 1); /* 如果文件后缀名 fileExtName 不是我们所需要的 就直接return,不处理,告诉用户文件类型不对。 */ System.out.println("文件信息 [件名:"+fileName+"---文件类型"+fileExtName+"]"); //可以使用UUID(唯一识别的通用码),保证文件名唯一; //UUID.randomUUID(),随机生一个唯一识别的通用码; String uuidPath = UUID.randomUUID().toString(); //===============处理文件完毕==============// //存到哪? uploadPath //文件真实存在的路径 realPath String realPath = uploadPath+"/"+uuidPath; //给每个文件创建一个对应的文件夹 File realPathFile = new File(realPath); if (!realPathFile.exists()){ realPathFile.mkdir(); } //===========存放地址完毕=============// //获得文件上传的流 InputStream inputStream = fileItem.getInputStream(); //创建一个文件输出流 //realPath = 真实的文件夹; //差了一个文件; 加上输出文件的名字+"/"+uuidFileName FileOutputStream fos = new FileOutputStream(realPath+"/"+fileName); //创建一个缓冲区 byte[] buffer = new byte[1024*1024]; //判断是否读取完毕 int len = 0; //如果大于0说明还存在数据; while ((len=inputStream.read(buffer))>0){ fos.write(buffer,0,len); } //关闭流 fos.close(); inputStream.close(); msg = "文件上传成功!"; fileItem.delete(); //上传成功,清除临时文件 //======================文件传输完毕============// } } return msg; } }
-
上传文件的前端页面,
upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>上传文件</title> </head> <%--method必须是post,应为get无法获取大数据的文件,post对象上传的额数据没有限制 entype="multipart/form-data":表示表单中存在上传数据 --%> <body> <form action="${pageContext.request.contextPath}/upload.do" enctype="multipart/form-data" method="post"> <p>上传用户:<input type="text" name="username"></p><br/> <p>上传文件:<input type="file" name="uploadFile"></p><br/> <P><input type="submit" value="提交"></P> </form> </body> </html>
-
消息提醒前端页面,
info.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>消息提醒</title> </head> <body> ${msg} </body> </html>
-
启动进入的页面,
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>提示信息</title> </head> <body> <a href="${pageContext.request.contextPath}/upload.jsp">进入页面</a> </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>org.javaweb.upload.UploadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>UploadServlet</servlet-name> <url-pattern>/upload.do</url-pattern> </servlet-mapping> </web-app>
3.3 结果分析
-
启动Tomcat,进入起始页面
index.jsp
点击“进入上传页面”。
-
跳转到上传文件的前端页面,
upload.jsp
,选择要上传的文件,点击上传。
-
执行文件上传代码
UploadServlet
,上传成功后,跳转到消息提醒页面,info.jsp
。 -
上传后的文件存放在
out/artifacts/javaweb_war_exploded/WEB-INF/upload
目录下。
4. 注意事项
4.1 文件上传注意事项
- 1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。
- 2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
- 3、要限制上传文件的最大值。
- 4、可以限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。
4.2 项目配置问题
-
配置
-
将生成的项目 javaweb_war_exploded(在out/artifacts下)放到Apache-tomcat的webapps目录下,可直接启动Tomcat运行项目。
Apache-tomcat的webapps目录下专门存放web项目。