• 《JavaWeb从入门到改行》fileupload,没毛病


     目录:

     »  fileupload API

     >  文件上传的要求

     >  fileupload组件

     »  上传细节的代码演示

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


    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组件的使用步骤如下:

    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: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

  • 相关阅读:
    【webpack 系列】进阶篇
    【webpack 系列】基础篇
    手写 Promise 符合 Promises/A+规范
    React-redux: React.js 和 Redux 架构的结合
    Redux 架构理解
    javascript 中的 this 判定
    编译原理
    vue 响应式原理
    强大的版本管理工具 Git
    js实现跨域(jsonp, iframe+window.name, iframe+window.domain, iframe+window.postMessage)
  • 原文地址:https://www.cnblogs.com/zyuqiang/p/7494296.html
Copyright © 2020-2023  润新知