目录:
> 文件上传的要求
fileupload API
文件上传的要求
上传文件对页面的要求:
- 必须使用表单,不能用超链接
- 表单的method必须是post, 不能是GET
- 表单的enctype必须是multipart/form-data
- 表单字段<input type="file"....>
<form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
用户名:<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>
上传文件对Servlet的要求:
- 当一个表单中存在普通表单项字段和文件表单项字段时,就不能使用request.getParameter(String) 来获取了,因为request.getParameter(String) 获取的是字符内容,而上传的文件是字节内容,所以要使用request.getInputStream()来得到ServletInputStream对象,(InputStream的子类)。 ServletInputStream对象对应所有表单数据(包含文件项和普通项)。 所以当有文件作为表单项时,我们需要操作的是ServletInputStream对象
fileupload组件
ServletInputStream对象是个流对象,我们需要去解析流中的数据,过于麻烦 。所以利用apache提供的Commons FileUpload 组件,这个组件能帮助我们方便的解析 。
Commons FileUpload 组件 包含 两个包: commons-fileupload.jar(核心包) 、 commons-io.jar (依赖包),如果使用组件,则必须导入这个两个包 。 (两个包的下载,在本文项目案例下载中)
fileupload的核心类有 : DiskFileItemFactory(工厂) 、ServletFileUpload(解析器) 、 FileItem(表单项)
fileupload组件的使用步骤如下:
- 创建工厂类DiskFileItemFactory 对象 : DiskFileItemFactory factory = new DiskFileItemFactory()
- 使用工厂创建解析器对象 : ServletFileUpload fileUpload = new ServletFileUpload(factory)
- 使用解析器来解析request对象:List<FileItem> list = fileUpload.parseRequest(request)
FileItem类是我们要的结果,一个FileItem对象对应一个表单项(一个表单字段),一个表单中存在文件字段和普通字段 , 该类的一些方法如下:
String getName() | 获取文件字段的文件名称 |
String getString(String charset) | 返回表单项的值 |
boolean isFormField() | 是否为普通表单项!返回true为普通表单项 |
String getFieldName() | 返回当前表单项的名称 |
long getSize() | 返回上传文件的字节数 |
InputStream getInputStream() | 返回上传文件对应的输入流 |
void write(File destFile) | 把上传的文件内容保存到指定的文件中 |
String getContentType() | 获取MIME类型 |
上传细节的代码演示
代码中 包含的内容 : 上传表单项与普通表单项 + 目录打散 + 缓存 + 文件大小限制
运行代码前: 应现在项目的WEB-INF目录下创建files目录 。 在D盘下创建temp目录
1 <h1>上传Demo1</h1> 2 <h3>${msg }</h3> 3 <form action="<c:url value='/UploadServlet'/>" method="post" enctype="multipart/form-data"> 4 用户名:<input type="text" name="username"/><br/> 5 照 片:<input type="file" name="zhaopian"/><br/> 6 <input type="submit" value="上传"/> 7 </form>
1 package cn.kmust.web.demo1.UpLoadServlet; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.List; 6 7 import javax.servlet.ServletException; 8 import javax.servlet.http.HttpServlet; 9 import javax.servlet.http.HttpServletRequest; 10 import javax.servlet.http.HttpServletResponse; 11 12 import org.apache.commons.fileupload.FileItem; 13 import org.apache.commons.fileupload.FileUploadBase; 14 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 15 import org.apache.commons.fileupload.servlet.ServletFileUpload; 16 17 import cn.itcast.commons.CommonUtils; 18 /** 19 * 处理上传的图片 20 * @功能 保存到web-inf下的files文件中,目录自动生成 21 * 有缓存的设置 22 * 有文件大小限制设置 23 * 目录打散 : 能够自动保存到web-inf下的files文件中创建目录,保存文件 24 * 能够保存到本地硬盘中 25 * @author ZHAOYUQIANG 26 * 27 */ 28 public class UploadServlet extends HttpServlet { 29 30 public void doPost(HttpServletRequest request, HttpServletResponse response) 31 throws ServletException, IOException { 32 request.setCharacterEncoding("utf-8"); 33 response.setContentType("text/html;charset=utf-8"); 34 /* 35 * 1. 得到工厂 36 * 设置缓存大小和临时目录: 上传的时候是存到内存的,如果过大,内存会吃不消, 37 * 需要先在硬盘中(D:/temp)存一下,每20*1024就往硬盘中存一下 38 * 39 * 2. 通过工厂创建解析器 40 * 对大小进行限制 ,放在解析之前 41 * 如果超过大小会抛出异常,抛出异常的类型不同 42 */ 43 DiskFileItemFactory factory = new DiskFileItemFactory(50*1024,new File("D:/temp")); 44 ServletFileUpload sfu = new ServletFileUpload(factory); 45 /** 46 * 功能1. 对单个文件大小和整个表单大小进行限制 47 * 并且在后面的异常中进行提示 48 */ 49 //限制单个文件大小为50KB,抛出FileUploadBase.FileSizeLimitExceededException 50 sfu.setFileSizeMax(50*1024);//并且限制单个文件大小为5M,抛出文件大小限制异常 51 //限制整个表单大小为50KB,抛出FileUploadBase.SizeLimitExceededException 52 //sfu.setSizeMax(50*1024); 53 try { 54 /* 55 * 3. 解析request,得到FileItem集合 56 * 4. 遍历FileItem集合,得到普通表单项(用户名等)和 57 * 文件表单项(上传的文件) 58 */ 59 List<FileItem> fileItemList = sfu.parseRequest(request); 60 FileItem fi1 = fileItemList.get(0); //得到第一个表单项 61 FileItem fi2 = fileItemList.get(1); //得到第二个表单项 62 //////////////////////////演示普通表单项与文件表单项///////////////////////////////// 63 /////////////////////////////////////////////////////////////////////////////// 64 /* 65 * 5. 输出普通表单项的演示 66 * 输出普通表单项的名和值 67 * 68 */ 69 System.out.println("@@@@@@@@@"); 70 System.out.println("普通表单项演示 : "+fi1.getFieldName()+ 71 "="+fi1.getString("UTF-8")); 72 /* 73 * 6. 输出文件表单项的相关演示 74 * 对于文件表单项:获取MIME类型、获取大小、获取文件名字 75 */ 76 System.out.println("文件表单项演示 :"); 77 System.out.println("Content-Type :"+fi2.getContentType()); 78 System.out.println("size :"+fi2.getSize()); 79 System.out.println("filename :"+fi2.getName()); 80 81 ////////////////////////////保存文件到web-inf下的files目录//////////////////////// 82 ////////////////////////////////////////////////////////////////////////////// 83 /** 84 * 1. 保存文件 [目录打散] 85 * 保存到web-inf下的files,这样安全 86 * 自动保存,文件目录自动创建,按照哈希打散生成两级目录, 87 * 分别取的十六进制前两个字符当作目录的一级和两级 88 */ 89 /* 90 * 1.1. 得到文件保存根路径 91 * 1.2. 得到文件名称 92 * 1.3. 进行两步处理 93 * 1.4. 用文件名得到整形的hashCode,把hashCode转换成十六进制 94 * 1.5. 获取十六进制前两个字符与根路径连接起来生成完成的目录路径 95 * 1.6. 路径有了之后,根据路径在磁盘上创建目录链 96 * 如果有了该目录什么也不做,如果没有就创建 97 * 1.7. 保存文件 98 */ 99 String root = this.getServletContext().getRealPath("/WEB-INF/files/"); 100 String filename = fi2.getName(); 101 /** 102 * 解决可能遇到的问题1 : 103 * 有的浏览器得到的文件名是绝对路径c:XXa.jpg,我们只是需要后截的路径 104 * 如果得到的是绝对路径,需要截取后半段的路径 105 */ 106 int index = filename.lastIndexOf("\"); //转义 107 if(index != -1){ //说明有,说明是绝对路径 108 filename = filename.substring(index+1);//截取出后半段的路径 109 } 110 /** 111 * 解决可能遇到的问题2 : 112 * 文件目录是自动生成的,可能存在文件同名问题 113 * 在生成的文件目录前面加上不同的id(利用commons中的uuid来生成) 114 */ 115 String savename = CommonUtils.uuid()+"_"+filename; 116 int hCode = filename.hashCode(); 117 String hex = Integer.toHexString(hCode); 118 File dirFile = new File(root,hex.charAt(0)+"/"+hex.charAt(1)); 119 120 dirFile.mkdirs(); 121 File destFile = new File(dirFile,savename); 122 try { 123 fi2.write(destFile); 124 } catch (Exception e) { 125 throw new RuntimeException(e); 126 } 127 } catch (Exception e) { 128 /* 129 * 处理单个文件大小限制异常 130 */ 131 if(e instanceof FileUploadBase.FileSizeLimitExceededException){ 132 request.setAttribute("msg", "您上传的文件超出了50KB!"); 133 request.getRequestDispatcher("/formDemo1.jsp").forward(request, response); 134 } 135 throw new ServletException(e); 136 } 137 } 138 }
注意 : 文件大小对目录的限制,如果超过规定的大小太多,则直接报异常错误,页面也显示无法访问此网站等信息。这是因为common-fileupload组件默认最大支持上传文件的大小为2M,当我们上传大于2M的文件时,这个异常的发生导致了fileUpload拦截器没有机会执行 。
上传的文件要到tomcat下项目的相关目录中查看
项目案例---上传头像并显示(MVC+MySQL)
CREATE TABLE `tb_employe` ( `eid` char(32) NOT NULL, `ename` char(100) NOT NULL, `gender` varchar(10) DEFAULT NULL, `image` varchar(200) DEFAULT NULL, PRIMARY KEY (`eid`) )
重要代码演示: (全部代码请下载项目查看,注意修改c3p0文件的数据库名称)
1 <h1><center>添加员工</center></h1> 2 <form action="<c:url value='/EmployeAddServlet'/>" method="post" enctype="multipart/form-data"> 3 编号 :<input type="text" name="eid" /><br/> 4 姓名: <input type="text" name ="ename"/><br/> 5 性别: <input type="radio" name="gender" value="男" />男 6 <input type="radio" name="gender" value="女"/>女 <br> 7 头像: <input type="file" name="image" /> <br/> 8 <input type="submit" value="添加"/> 9 </form>
1 <c:forEach items="${employeList }" var="employe"> 2 <div class="icon"> 3 <img src="<c:url value='/${employe.image }'/>" border="0"/><br/> 4 <center> 5 <table border="0"> 6 <tr> 7 <td>编号: </td><td>${employe.eid }</td> 8 </tr> 9 <tr> 10 <td>姓名: </td><td>${employe.ename }</td> 11 </tr> 12 <tr> 13 <td>性别: </td><td>${employe.gender }</td> 14 </tr> 15 </table> 16 </center> 17 </div> 18 </c:forEach>
package cn.kmust.employe.web.servlet; import java.awt.Image; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.swing.ImageIcon; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import cn.itcast.commons.CommonUtils; import cn.kmust.employe.domain.Employe; import cn.kmust.employe.service.EmployeService; /** * web层 * @author ZHAOYUQIANG * */ public class EmployeAddServlet extends HttpServlet { private EmployeService employeService = new EmployeService(); public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); /* * 上传三步 * 创建工厂、得到解析器、使用解析器去解析request对象,得到List<FileItem> */ DiskFileItemFactory factory = new DiskFileItemFactory(100*1024,new File("D:/temp")); ServletFileUpload sfu = new ServletFileUpload(factory); /* * 校验 头像是否>50KB */ //sfu.setFileSizeMax(50*1024); try{ List<FileItem> fileItemList = sfu.parseRequest(request); /* * 把fileItemList中的数据封装到Employe对象中 */ Map<String,String> map = new HashMap<String,String>(); /* * 首先把所有的普通表单字段先封装到Map中,再把map中的数据封装到Employe中 */ for(FileItem fileItem : fileItemList){ if(fileItem.isFormField()){ //判断是不是普通表单项 map.put(fileItem.getFieldName(), fileItem.getString("UTF-8")); } } Employe employe = CommonUtils.toBean(map, Employe.class); /* * 保存上传的文件,即把上传的头像图片保存到带有盘符的目录中 * 要保存的路径 * 要保存的文件名称 */ String savepath = this.getServletContext().getRealPath("/images");//得到保存目录 //得到文件名称,给文件名称添加uuid前缀,避免名称冲突。 //需要注意: get(3)表示文件表单项在表单的第3个位置3 String filename = CommonUtils.uuid()+"_"+fileItemList.get(3).getName(); // for(int i =0;i<30;i++){ // System.out.println(i+":"+fileItemList.get(i).getName()); // } /* * 校验文件扩展名是否是jpg格式的 */ if(!filename.toLowerCase().endsWith("jpg")){ request.setAttribute("msg", "您上传的图片不是jpg扩展名!"); request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response); return ; } File destFile = new File(savepath,filename); //使用目录和文件名创建目标文件 fileItemList.get(3).write(destFile);//保存上传文件到目标位置 /* * 设置Employe对象的image */ employe.setImage("images/"+filename); /* * 调用service完成添加 */ System.out.println("3333333333333333333"); employeService.add(employe); /* * 校验图片的尺寸 */ Image image = new ImageIcon(destFile.getAbsolutePath()).getImage(); if(image.getWidth(null)>200 || image.getHeight(null)>200){ //删除图片 destFile.delete(); request.setAttribute("msg", "您上传的图片尺寸超过了200*200"); request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response); return ; } /* * 完成添加,返回员工列表 */ request.getRequestDispatcher("/EmployeServlet?method=findAll").forward(request, response); }catch(Exception e){ /* * 处理上传图片过程中出现的图片大小异常 */ if(e instanceof FileUploadBase.FileSizeLimitExceededException){ request.setAttribute("msg", "您上传的图片超过了50KB"); request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response); } } } }
项目下载 : https://files.cnblogs.com/files/zyuqiang/fileuploadProject.zip