目录:

»  fileupload API

文件上传的要求

fileupload组件

»  上传细节的代码演示

»  项目案例-上传头像并显示


fileupload API

文件上传的要求

上传文件对页面的要求:

  • 必须使用表单,不能用超链接
  • 表单的method必须是post, 不能是GET
  • 表单的enctype必须是multipart/form-data
  • 表单字段<input type="file"....>
  1. <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">
  2. 用户名:<input type="text" name="username"/><br/>
  3. 文件1:<input type="file" name="file1"/><br/>
  4. 文件2:<input type="file" name="file2"/><br/>
  5. <input type="submit" value="提交"/>
  6. </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组件的使用步骤如下:

  1. 创建工厂类DiskFileItemFactory 对象 : DiskFileItemFactory factory = new DiskFileItemFactory()
  2. 使用工厂创建解析器对象   : ServletFileUpload fileUpload = new ServletFileUpload(factory)
  3. 使用解析器来解析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:\XX\a.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)

  1. CREATE TABLE `tb_employe` (
  2. `eid` char(32) NOT NULL,
  3. `ename` char(100) NOT NULL,
  4. `gender` varchar(10) DEFAULT NULL,
  5. `image` varchar(200) DEFAULT NULL,
  6. PRIMARY KEY (`eid`)
  7. )

重要代码演示: (全部代码请下载项目查看,注意修改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>
  1. package cn.kmust.employe.web.servlet;
  2.  
  3. import java.awt.Image;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.util.HashMap;
  7. import java.util.List;
  8. import java.util.Map;
  9.  
  10. import javax.servlet.ServletException;
  11. import javax.servlet.http.HttpServlet;
  12. import javax.servlet.http.HttpServletRequest;
  13. import javax.servlet.http.HttpServletResponse;
  14. import javax.swing.ImageIcon;
  15.  
  16. import org.apache.commons.fileupload.FileItem;
  17. import org.apache.commons.fileupload.FileUploadBase;
  18. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  19. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  20.  
  21. import cn.itcast.commons.CommonUtils;
  22. import cn.kmust.employe.domain.Employe;
  23. import cn.kmust.employe.service.EmployeService;
  24.  
  25. /**
  26. * web层
  27. * @author ZHAOYUQIANG
  28. *
  29. */
  30. public class EmployeAddServlet extends HttpServlet {
  31. private EmployeService employeService = new EmployeService();
  32.  
  33. public void doPost(HttpServletRequest request, HttpServletResponse response)
  34. throws ServletException, IOException {
  35. request.setCharacterEncoding("utf-8");
  36. response.setContentType("text/html;charset=utf-8");
  37.  
  38. /*
  39. * 上传三步
  40. * 创建工厂、得到解析器、使用解析器去解析request对象,得到List<FileItem>
  41. */
  42. DiskFileItemFactory factory = new DiskFileItemFactory(100*1024,new File("D:/temp"));
  43. ServletFileUpload sfu = new ServletFileUpload(factory);
  44. /*
  45. * 校验 头像是否>50KB
  46. */
  47. //sfu.setFileSizeMax(50*1024);
  48. try{
  49. List<FileItem> fileItemList = sfu.parseRequest(request);
  50. /*
  51. * 把fileItemList中的数据封装到Employe对象中
  52. */
  53. Map<String,String> map = new HashMap<String,String>();
  54. /*
  55. * 首先把所有的普通表单字段先封装到Map中,再把map中的数据封装到Employe中
  56. */
  57. for(FileItem fileItem : fileItemList){
  58. if(fileItem.isFormField()){ //判断是不是普通表单项
  59. map.put(fileItem.getFieldName(), fileItem.getString("UTF-8"));
  60. }
  61. }
  62. Employe employe = CommonUtils.toBean(map, Employe.class);
  63. /*
  64. * 保存上传的文件,即把上传的头像图片保存到带有盘符的目录中
  65. * 要保存的路径
  66. * 要保存的文件名称
  67. */
  68. String savepath = this.getServletContext().getRealPath("/images");//得到保存目录
  69. //得到文件名称,给文件名称添加uuid前缀,避免名称冲突。
  70. //需要注意: get(3)表示文件表单项在表单的第3个位置3
  71. String filename = CommonUtils.uuid()+"_"+fileItemList.get(3).getName();
  72. // for(int i =0;i<30;i++){
  73. // System.out.println(i+":"+fileItemList.get(i).getName());
  74. // }
  75. /*
  76. * 校验文件扩展名是否是jpg格式的
  77. */
  78. if(!filename.toLowerCase().endsWith("jpg")){
  79. request.setAttribute("msg", "您上传的图片不是jpg扩展名!");
  80. request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
  81. return ;
  82. }
  83. File destFile = new File(savepath,filename); //使用目录和文件名创建目标文件
  84. fileItemList.get(3).write(destFile);//保存上传文件到目标位置
  85. /*
  86. * 设置Employe对象的image
  87. */
  88. employe.setImage("images/"+filename);
  89. /*
  90. * 调用service完成添加
  91. */
  92. System.out.println("3333333333333333333");
  93. employeService.add(employe);
  94. /*
  95. * 校验图片的尺寸
  96. */
  97. Image image = new ImageIcon(destFile.getAbsolutePath()).getImage();
  98. if(image.getWidth(null)>200 || image.getHeight(null)>200){
  99. //删除图片
  100. destFile.delete();
  101. request.setAttribute("msg", "您上传的图片尺寸超过了200*200");
  102. request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
  103. return ;
  104. }
  105. /*
  106. * 完成添加,返回员工列表
  107. */
  108. request.getRequestDispatcher("/EmployeServlet?method=findAll").forward(request, response);
  109. }catch(Exception e){
  110. /*
  111. * 处理上传图片过程中出现的图片大小异常
  112. */
  113. if(e instanceof FileUploadBase.FileSizeLimitExceededException){
  114. request.setAttribute("msg", "您上传的图片超过了50KB");
  115. request.getRequestDispatcher("/AddEmploye.jsp").forward(request, response);
  116. }
  117. }
  118. }
  119. }

项目下载 : https://files.cnblogs.com/files/zyuqiang/fileuploadProject.zip

《JavaWeb从入门到改行》fileupload,没毛病的更多相关文章

  1. 《JavaWeb从入门到改行》过滤器学习笔记

    >"; display: block; height: 0; clear: both; visibility: hidden; } #sitemap, #sitemap ul{disp ...

  2. 《JavaWeb从入门到改行》JSP+EL+JSTL大杂烩汤

    title: Servlet之JSP tags: [] notebook: javaWEB --- JSP是什么 ? JSP就是Servlet,全名是"JavaServer Pages&qu ...

  3. 《JavaWeb从入门到改行》JDBC经典秘方QueryRunner

    目录: 基础篇_功能各自回顾 JDBC基础代码回顾(使用JdbcUtils工具简化) c3p0数据库连接池的使用(使用JdbcUtils工具简化) 大数据的插入(使用c3p0+JdbcUtils工具简 ...

  4. 《JavaWeb从入门到改行》很好的复习资料: SQL语句到底怎么写 ?

    本文用到的数据库如下: CREATE DATABASE exam; /创建部门表/ CREATE TABLE dept( deptno INT PRIMARY KEY, dname ), loc ) ...

  5. 《JavaWeb从入门到改行》那些年一起学习的Servlet

    目录 获取ServletContext : ServletContext接口中的一些方法 application域存取数据功能 代码演示: application域获取项目文件路径 代码演示: API ...

  6. 《JavaWeb从入门到改行》注册时向指定邮箱发送邮件激活

    javaMail API javaMail是SUN公司提供的针对邮件的API . 两个jar包  mail.jar 和 activation.jar java mail中主要类:javax.mail. ...

  7. 《JavaWeb从入门到改行》多重外键关系在java中的处理方案

    目录:(点击红色方框展开子目录) 问题描述 无 项目案例说明 业务描述 数据库说明 项目源码及下载 无 问题描述 如上两图,数据库中各个表之间有很多的外键关系,其中业务关系是一个用户下有该用户的订单, ...

  8. 《JavaWeb从入门到改行》关于BaseServlet那些事

    @为什么需要BaseServlet?  我们知道一个POST或者GET提交对应着一个Servlet, 无数的提交会让Servlet页面增加,我们希望一个Servlet就能处理很多提交的请求. @Bas ...

  9. 《JavaWeb从入门到改行》分页功能的实现

    @目录 什么是分页 ? 两个子模块功能的问题分析 和 解决方案 有条件查和无条件查询的影响 和 解决方案 项目案例: mysql + commons-dbutils+itcast-tools+Base ...

随机推荐

  1. javascript获取wx.config内部字段解决微信分享

    转自:http://www.jb51.net/article/80679.htm 专题推荐:js微信开发_脚本之家 http://www.jb51.net/Special/879.htm 背景在微信分 ...

  2. python附录-re.py模块源码(含re官方文档链接)

    re模块 python官方文档链接:https://docs.python.org/zh-cn/3/library/re.html re模块源码 r"""Support ...

  3. SpringMVC初写(六)静态资源设置

    众所周知,SpringMVC的DispatchServlet是不可以以/*规则拦截请求的,否则会将JSP都拦截了,但有时候我们的请求路径是不能有后缀(Resful风格的接口需要),基于上述情况,我们可 ...

  4. Visual Studio 跨平台開發實戰(2) - Xamarin.iOS 基本控制項介紹 (转帖)

    前言 在上一篇文章中, 我們介紹了Xamarin 以及簡單的HelloWorld範例, 這次我們針對iOS的專案目錄架構以及基本控制項進行說明. 包含UIButton,, UISlider, UISw ...

  5. Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. 调用函数约定不同

    Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is ...

  6. javascript004_ECMA5数组新特性

    •对于ECMAscript5这个版本的Array新特性补充: –位置方法:indexOf      lastIndexOf –迭代方法:every  filter   forEach   some   ...

  7. MYSQL数据库的参数文件

    参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内存结构的大小等设置,还会介绍各种参数的类型. 参数文件 当MySQL实例启动时,MySQL会先去 ...

  8. JVM的类加载时机

    类加载过程中每个步骤的顺序 我们已经知道,类加载的过程包括:加载.连接.初始化,连接又分为:验证.准备.解析,所以说类加载一共分为5步:加载.验证.准备.解析.初始化. 其中加载.验证.准备.初始化的 ...

  9. 一头扎进sql之多表操作

    一.多表查询时NULL值处理 要求返回比"allen"工资低的所有员工 select  a.ename,a.conn from emp a  where  a.conn  < ...

  10. 我爱Markdown (2)

    Markdown的语法很简单,所以很容易上手.下面介绍一下常用的Markdown的语法, 本文将介绍: 01 - Back-ticks 反尖号 02 - Headers 标题 03 - Emphasi ...