• 文件上传表单 上传文件的细节 文件上传下载和数据库结合


    1 文件上传表单
      1)上传文件的本质是文本复制的过程
      2)技术层面,在Java中一定会用到IO操作,主要以二进制方式读写
      3)传统方式下,对于上传文件字段不同的浏览器有着不同的解析方式,例如:
        IE6:upfile=c:aaba.JPG
        非IE6: upfile=a.JPG
      4)可以将form以MIME协议的方式将上传文件传递到服务端,服务端以二进制流的方式读写
        代码:客户端form enctype="multipart/form-data"/>
                服务端request.getInputStream()

    Tip:文件上传概述

        实现web开发中的文件上传功能,需完成如下二步操作:

            •在web页面中添加上传输入项

            •在servlet中读取上传文件的数据,并保存到本地硬盘中。

        如何在web页面中添加上传输入项?

            •<input type=“file”>标签用于在web页面中添加文件上传输入项,设置文件上传输入项时须注意:

            •1、必须要设置input输入项的name属性,否则浏览器将不会发送上传文件的数据。

            •2、必须把form的enctype属值设为multipart/form-data.设置该值后,浏览器在上传文件时,将把文件数据附带在http请求消息体中,并使用MIME协议对上传的文件进行描述,以方便接收方对上传数据进行解析和处理。

        如何在Servlet中读取文件上传数据,并保存到本地硬盘中?

            •request对象提供了一个getInputStream方法,通过这个方法可以读取到客户端提交过来的数据。但由于用户可能会同时上传多个文件,在servlet端编程直接读取上传数据,并分别解析出相应的文件数据是一项非常麻烦的工作,示例。

            •为方便用户处理文件上传数据,Apache 开源组织提供了一个用来处理表单文件上传的一个开源组件( Commons-fileupload ),该组件性能优异,并且其API使用极其简单,可以让开发人员轻松实现web文件上传功能,因此在web开发中实现文件上传功能,通常使用commons-fileupload组件实现。

        使用commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:commons-fileupload和commons-io。commons-io 不属于文件上传组件的开发jar文件,但commons-fileupload 组件从1.1 版本开始,它工作时需要commons-io包的支持。

    Tip:fileupload组件工作流程

    image

    Tip:核心API—DiskFileItemFactory

        DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:

        •public void setSizeThreshold(int sizeThreshold)

            •设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。

        •public void setRepository(java.io.File repository)

            •指定临时文件目录,默认值为System.getProperty("java.io.tmpdir").

        •public DiskFileItemFactory(int sizeThreshold, java.io.File repository)

            •构造函数

    Tip:核心API—ServletFileUpload

        ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:

        •boolean isMultipartContent(HttpServletRequest request)

            •判断上传表单是否为multipart/form-data类型

        •List parseRequest(HttpServletRequest request)

            •解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。

        •setFileSizeMax(long fileSizeMax)

            •设置上传文件的最大值(单位字节)

        •setSizeMax(long sizeMax)

            •设置上传文件总量的最大值

        •setHeaderEncoding(java.lang.String encoding)

            •设置编码格式,解决上传中文名文件的问题

    Tip:文件上传案例

        实现步骤

        1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录

        2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。

        3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容FileItem的List对象。

        4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件

        •为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值

        •为上传文件,则调用getName 、 getInputStream方法得到数据输入流,从而读取上传数据。

    Tip:上传文件的处理细节

        中文文件名乱码问题

            •文件名中文乱码问题,可调用ServletFileUpload的setHeaderEncoding方法,或者设置request的setCharacterEncoding属性

        临时文件的删除问题

            •由于文件大小超出DiskFileItemFactory.setSizeThreshold方法设置的内存缓冲区的大小时,commons-fileupload组件将使用临时文件保存上传数据,因此在程序结束时,务必调用FileItem.delete方法删除临时文件。

            delete方法的调用必须位于流关闭之后,否则会出现文件占用,而导致删除失败的情况。

        文件存放名字

            •为防止多用户上传相同文件名的文件,而导致文件覆盖的情况发生,文件上传程序应保证上传文件具有唯一文件名。

        文件存放位置

            •为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录。

            •为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应根据可能的文件上传总量,选择合适的目录结构生成算法,将上传文件分散存储到不同的目录。

    <%@ page language="java" pageEncoding="UTF-8"%>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <head>
          <script type="text/javascript">
              //全局变量
              var time = 0;
              function addLine(addButton){
                  //创建内部div对象
                  var divElement = document.createElement("div");
                  //创建input对象[file类型]
                  var inputElement1 = document.createElement("input");
                  inputElement1.type="file";
                  inputElement1.name="upfile";
                  //创建input对象[button类型]
                  var inputElement2 = document.createElement("input");
                  inputElement2.type="button";
                  inputElement2.value="删除";
                  //对删除按钮添加事件监听
                  inputElement2.onclick=function(){
                      //取得该按钮所在行的直接父元素
                      var divElement = this.parentNode.parentNode;
                      //通过父元素删除直接子元素
                      divElement.removeChild(this.parentNode);
                      time--;
                      if(time < 5){
                          //按钮生效
                          addButton.disabled=false;
                          //addButton.style.visibility="visible";
                      }
                  }
                  //依次将file类型和button类型的input对象加入到内部div对象中
                  divElement.appendChild(inputElement1);
                  divElement.appendChild(inputElement2);
                  //再次内部div对象加入到外部div对象
                  var outDivElement = document.getElementById("outDiv");
                  outDivElement.appendChild(divElement);
                  time++;
                  if(time == 5){
                      //将按钮失效
                      addButton.disabled=true;
                      //addButton.style.visibility="hidden";
                  }    
              }
          </script>
      </head>
      <body>
          <form 
              action="${pageContext.request.contextPath}/UploadServlet" 
              method="POST"
              enctype="multipart/form-data">
              <table border="1" align="center">
                  <caption>文件上传</caption>
                  <tr>
                      <th>上传用户</th>
                      <td><input type="text" name="username"/></td>
                  </tr>
                  <tr>
                      <th></th>
                      <td>
                          <div id="outDiv">
                              <%--
                              <div>
                                <input type="file" name="upfile"/>
                                <input type="button" value="删除"/>                              
                              </div>
                              --%>
                          </div>
                      </td>
                  </tr>
                  <tr>
                      <th></th>
                      <td>
                          <input 
                              type="button" 
                              value="添加上传文件"
                              onclick="addLine(this)"
                          />
                      </td>
                  </tr>
                  <tr>
                      <td colspan="2" align="center">
                          <input type="submit" value="上传"/>
                          <a href="${pageContext.request.contextPath}/ListFileServlet">
                              显示下载文件
                          </a>
                      </td>
                  </tr>
              </table>
          </form>
      </body>
    </html>
    package cn.itcast.web.servlet.upload;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.List;
    import java.util.UUID;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    
    public class UploadServlet2 extends HttpServlet {
        public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            //取得上传使用的临时和真实目录
            String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
            String uploadPath = this.getServletContext().getRealPath("/WEB-INF/upload");
            //创建上传文件工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置内存中缓存区的大小,默认10K
            factory.setSizeThreshold(100*1024);
            //设置上传文件临时存放的目录
            factory.setRepository(new File(tempPath));
            //创建上传文件对象[核心]
            ServletFileUpload upload = new ServletFileUpload(factory);
            //设置上传文件的中文编码方式
            upload.setHeaderEncoding("UTF-8");
            //客户端上传文件是否使用MIME协议,
            boolean flag = upload.isMultipartContent(request);
            if(!flag){
                //不是以MIME协议上传文件
                throw new ServletException();
            }else{
                /*是以MIME协议上传的文件,解析request中的所有上传内容
                 *每个内容封装成一个对象FileItem,
                 *FileItem代表普通字段和上传字段二类
                 */
                try {
                    List<FileItem> fileItemList  = upload.parseRequest(request);
                    for(FileItem fileItem : fileItemList){
                        if(fileItem.isFormField()){
                            //必定是普通字段
                            String fieldName = fileItem.getFieldName();
                            String fieldValue = fileItem.getString("UTF-8");
                            System.out.println(fieldName+":"+fieldValue);
                        }else {
                            //必定是上传字段
                            String realFileName = fileItem.getName();
                            int index = realFileName.lastIndexOf("\");
                            if(index>=0){
                                //IE6浏览器
                                realFileName = realFileName.substring(index+1);
                            }
                            //通过真实文件名换算出唯一个文件名
                            String uuidFileName = makeUuidFileName(realFileName);
                            //通过位运算换算出upload目录下的子目录
                            String uuidFilePath = makeUuidFilePath(uploadPath,uuidFileName);
                            //取得文件输入流
                            InputStream is = fileItem.getInputStream();
                            //取得文件输出流
                            OutputStream os = new FileOutputStream(uuidFilePath+"/"+uuidFileName);
                            byte[] buf = new byte[1024];
                            int len = 0;
                            while( (len=is.read(buf))>0 ){
                                os.write(buf,0,len);
                            }
                            is.close();
                            os.close();
                            //将上传文件产生的临时文件删除
                            fileItem.delete();
                            request.setAttribute("message","上传文件成功");
                            request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
                        }
                    }
                
                } catch (Exception e) {
                    e.printStackTrace();
                    request.setAttribute("message","上传文件失败");
                    request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
                }
            }        
            /*方式二
            InputStream is = request.getInputStream();
            OutputStream os = response.getOutputStream();
            byte[] buf = new byte[1024];
            int len = 0;
            while( (len=is.read(buf))>0 ){
                os.write(buf,0,len);
            }
            is.close();
            os.close();
            */
            /*方式一
            String username = request.getParameter("username");
            String upfile = request.getParameter("upfile");
            System.out.println("上传用户:" + username);
            System.out.println("上传文件:" + upfile);
            */
        }
        /*
         * uploadPath="/upload" 
         * uuidFileName="fdafdsfdsa_a.JPG"
         * return /upload/8/a 比较难,没看懂
         */
    
    private String makeUuidFilePath(String uploadPath, String uuidFileName) { String uuidFilePath = null; int code = uuidFileName.hashCode();//8 int dir1 = code & 0xF;//3 int dir2 = code >> 4 & 0xF;//A File file = new File(uploadPath+"/"+dir1+"/"+dir2); //如果该目录未存在 if(!file.exists()){ //一次性创建N层目录 file.mkdirs(); } uuidFilePath = file.getPath(); return
     uuidFilePath;
        }
        /*
         * realFileName="a.JPG"
         * return "fdafdsfdsa_a.JPG"
         */
        private String makeUuidFileName(String realFileName) {
            return UUID.randomUUID().toString()+"_"+realFileName;
        }
    }

    *2 上传文件的细节

      (1)中文乱码的问题
         a)普通字段的中文乱码问题:FileItem.getString("UTF-8"),
           注意:FileItem表示上传表单中的表单项内容
         b)上传字段的中文乱码问题:ServletUploadFile.setHeaderEncoding("UTF-8");

      (2)临时文件的删除的问题
         a)通过FileItem.delete()
         b)一定要在关闭IO流之后

      (3)在同一个目录下上传相同文件名的问题
         a)将文件名拼接一个唯一标识符,即UUID

      (4)单个目录下文件过多的问题
         a)采用位运算解决单个目录文件过多
         b)思路:参见<<>>

      (5)为安全将上传的文件放入客户端无法直接访问的目录中的问题
         a)将上传的文件,放置到/WEB-INF/upload/目录下
      (6)重构思想
         a)做到通用性

    package cn.itcast.web.servlet.util;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.List;
    import java.util.UUID;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    
    import cn.itcast.web.servlet.domain.Up;
    import cn.itcast.web.servlet.domain.User;
    import cn.itcast.web.servlet.exception.NoUpfileException;
    import cn.itcast.web.servlet.exception.UpfileSizeException;
    
    public final class UploadUtil {
        
        //取得上传使用的临时和真实目录
        public static final String tempPath = "/WEB-INF/temp";
        public static final String uploadPath = "/WEB-INF/upload";
        
        //取得真实文件名
        public static String getRealFileName(String realFileName){
            int index = realFileName.lastIndexOf("\");
            if(index>=0){
                //IE6浏览器
                realFileName = realFileName.substring(index+1);
            }
            return realFileName;
        }
        
        //取得uuid文件名
        public static String makeUuidFilePath(String uploadPath, String uuidFileName) {
            String uuidFilePath = null;
            int code = uuidFileName.hashCode();//8
            int dir1 = code & 0xF;//3
            int dir2 = code >> 4 & 0xF;//A 
            File file = new File(uploadPath+"/"+dir1+"/"+dir2);
            //如果该目录未存在
            if(!file.exists()){
                //一次性创建N层目录
                file.mkdirs();
            }
            uuidFilePath = file.getPath();
            return uuidFilePath;
        }
        
        //取得upload/目录下的分散目录
        public static String makeUuidFileName(String realFileName) {
            return UUID.randomUUID().toString()+"_"+realFileName;
        }
        
        //文件复制
        public static void doSave(InputStream is,String uuidFileName,String uuidFilePath){
            OutputStream os = null;
            try {
                os = new FileOutputStream(uuidFilePath+"/"+uuidFileName);
                byte[] buf = new byte[1024];
                int len = 0;
                while( (len=is.read(buf))>0 ){
                    os.write(buf,0,len);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                if(is!=null){
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(os!=null){
                    try {
                        os.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } 
        }
        
        //将上传文件封装成JavaBean对象中
        public static User doUpload(HttpServletRequest request) throws Exception{
            User user = new User();
            //创建上传文件工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            //设置内存中缓存区的大小,默认10K
            factory.setSizeThreshold(100*1024);
            //设置上传文件临时存放的目录
            String tempPath = request.getSession().getServletContext().getRealPath(UploadUtil.tempPath);
            factory.setRepository(new File(tempPath));
            //创建上传文件对象[核心]
            ServletFileUpload upload = new ServletFileUpload(factory);
            //设置上传文件的中文编码方式
            upload.setHeaderEncoding("UTF-8");
            //客户端上传文件是否使用MIME协议,
            boolean flag = upload.isMultipartContent(request);
            if(!flag){
                //不是以MIME协议上传文件
                throw new ServletException();
            }else{
                /*是以MIME协议上传的文件,解析request中的所有上传内容
                 *每个内容封装成一个对象FileItem,
                 *FileItem代表普通字段和上传字段二类
                 */
                List<FileItem> fileItemList  = upload.parseRequest(request);
                for(FileItem fileItem : fileItemList){
                    if(fileItem.isFormField()){
                        //必定是普通字段
                        String fieldName = fileItem.getFieldName();
                        String fieldValue = fileItem.getString("UTF-8");
                        user.setUsername(fieldValue);
                    }else {
                        //必定是上传字段    
                        //如果无上传文件
                        if(fileItem.getSize()==0){
                            throw new NoUpfileException();
                        }
                        String realFileName = UploadUtil.getRealFileName(fileItem.getName());    
                        /*只能上传JPG文件
                        if(!realFileName.endsWith("JPG")){
                            throw new UpfileTypeException();
                        }
                        */
                        //只有上传<=200K的文件
                        if(fileItem.getSize() > 200 * 1024){
                            throw new UpfileSizeException();
                        }
                        //封装到JavaBean
                        user.getUpfileList().add(fileItem);
                        
                    }
                }//end of for loop
            }
            return user;
        }
        
        public static void doSave(User user, String uploadPath,List<Up> upList) throws Exception {
            //取得该用户上传的所有文件集合
            List<FileItem> fileItemList = user.getUpfileList();
            //迭代每个文件,并上传
            for(FileItem fileItem : fileItemList){
                //创建Up对象
                Up up = new Up();
                up.setUsername(user.getUsername());
                //取得输入流
                InputStream is = fileItem.getInputStream();
                //取得真实文件名
                String realFileName = fileItem.getName();
                realFileName = UploadUtil.getRealFileName(realFileName);
                //取得UUID文件名
                String uuidFileName = UploadUtil.makeUuidFileName(realFileName);
                //取得UUID文件路径
                String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath,uuidFileName);
                //保存
                UploadUtil.doSave(is,uuidFileName,uuidFilePath);
                //收集Up信息
                up.setUuidFileName(uuidFileName);
                up.setRealFileName(realFileName);
                upList.add(up);
                //删除临时文件
                fileItem.delete();
            }
            
        }
    }
    package cn.itcast.web.servlet.upload;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import cn.itcast.web.servlet.domain.Up;
    import cn.itcast.web.servlet.domain.User;
    import cn.itcast.web.servlet.exception.NoUpfileException;
    import cn.itcast.web.servlet.exception.UpfileSizeException;
    import cn.itcast.web.servlet.exception.UpfileTypeException;
    import cn.itcast.web.servlet.service.UpService;
    import cn.itcast.web.servlet.util.UploadUtil;
    
    public class UploadServlet extends HttpServlet {
        public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            try {
                User user = UploadUtil.doUpload(request);
                String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
                
                List<Up> upList = new ArrayList<Up>();
                
                //写入硬盘
                UploadUtil.doSave(user,uploadPath,upList);
                
                //写入数据库表
                UpService upService = new UpService();
                upService.addUps(upList);
                
                request.setAttribute("message","上传文件成功");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(UpfileSizeException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='green'>上传文件大小限制在200K以内</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(UpfileTypeException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='red'>只能上传JPG格式的文件</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(NoUpfileException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='blue'>无上传文件</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch (Exception e) {
                e.printStackTrace();
                request.setAttribute("message","上传文件失败");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }
        }
    }
    package cn.itcast.web.servlet.domain;
    
    import java.util.ArrayList;
    import java.util.List;
    import org.apache.commons.fileupload.FileItem;
    
    //封装上传文件的内容
    public class User {
        //上传用户
        private String username;
        //上传的文件
        private List<FileItem> upfileList = new ArrayList<FileItem>(); 
        public User(){}
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public List<FileItem> getUpfileList() {
            return upfileList;
        }
        public void setUpfileList(List<FileItem> upfileList) {
            this.upfileList = upfileList;
        }
    }
    package cn.itcast.web.servlet.domain;
    
    public class Up {
         private int id;
         private String username;
         private String realFileName;
         private String uuidFileName;
         public Up(){}
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getRealFileName() {
            return realFileName;
        }
        public void setRealFileName(String realFileName) {
            this.realFileName = realFileName;
        }
        public String getUuidFileName() {
            return uuidFileName;
        }
        public void setUuidFileName(String uuidFileName) {
            this.uuidFileName = uuidFileName;
        }
    }

    (7)自定义封装上传文件工具类的问题

        课堂练习:无上传文件提交/只能上传JPG或jpg文件

      (8)上传文件的大小的问题

      (9)上传多个文件的问题

      (10)上传多个文件的界面问题

    Tip:多个文件上传的javascript编码

        技巧:

        •每次动态增加一个文件上传输入框,都把它和删除按纽放置在一个单独的div中,并对删除按纽的onclick事件进行响应,使之删除删除按纽所在的div。

        •如:this.parentNode.parentNode.removeChild(this.parentNode);
    *3 显示下载文件列表
      a)递归方式查询可供下载的文件,一定要有出口条件
      b)使用Map<UUID文件名,真实文件名>收集可供下载的文件
      c)使用<c:url>和<c:param>对中文名进行URL编码
    *4 下载文件
       a)对传过来的中文编码进行URL解码
       b)通过UUID文件名,反向查到该文件所在的真实目录
    5 文件上传下载与数据库结合
       a)在将上传文件保存的同时,写往数据库表,一个上传文件对应一条记录,确保uuidFileName双方一致
       思考:
        a)上传时,先硬盘,再表?
        b)下载时,先硬盘,再表?
        c)删除时,先硬盘,再表?
        d)是否需要事务支持?

    Tip:文件下载

        Web应用中实现文件下载的两种方式

        •超链接直接指向下载资源

        •程序实现下载需设置两个响应头:

        •设置Content-Type 的值为:application/x-msdownload。Web 服务器需要告诉浏览器其所输出的内容的类型不是普通的文本文件或 HTML 文件,而是一个要保存到本地的下载文件。

        •Web 服务器希望浏览器不直接处理相应的实体内容,而是由用户选择将相应的实体内容保存到一个文件中,这需要设置 Content-Disposition 报头。该报头指定了接收程序处理数据内容的方式,在 HTTP 应用中只有 attachment 是标准方式,attachment 表示要求用户干预。在 attachment 后面还可以指定 filename 参数,该参数是服务器建议浏览器将实体内容保存到文件中的文件名称。在设置 Content-Dispostion 之前一定要指定 Content-Type.

        •因为要下载的文件可以是各种类型的文件,所以要将文件传送给客户端,其相应内容应该被当做二进制来处理,所以应该调用 方法返回 ServletOutputStream 对象来向客户端写入文件内容。

    package cn.itcast.web.servlet.download;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.HashMap;
    import java.util.Map;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import cn.itcast.web.servlet.util.UploadUtil;
    
    //显示可供下载的文件列表
    public class ListFileServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            //定位下载文件的目录
            String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
            //创建Map<UUID文件名,真实文件名>
            Map<String,String> map = new HashMap<String,String>();
            //取得下载文件的相关信息
            getFiles(uploadPath,map);
            //转发到list.jsp显示可供下载的文件
            request.setAttribute("map",map);
            request.getRequestDispatcher("/WEB-INF/list.jsp").forward(request,response);
        }
        
        //递归询查所有可供下载的文件
        private void getFiles(String uploadPath , Map<String,String> map){
            File file = new File(uploadPath);
            //如果file表示文件
            if(file.isFile()){//出口
                  //取得文件名,即UUID文件名
                  String uuidFileName = file.getName();
                int index = uuidFileName.indexOf("_");
                String realFileName = uuidFileName.substring(index+1);
                //存放到Map集合中
                  map.put(uuidFileName,realFileName);
            }else{
                //必定是目录
                  //取得该目录下的所有内容
                  File[] files = file.listFiles();
                for(File f : files){
                    //递归调用自已
                    getFiles(f.getPath(),map);
                }
            }
        }
    }
    <%@ page language="java" pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    <html>
      <body>
          <table border="1" align="center">
              <caption>下载文件列表</caption>
              <tr>
                  <th>文件名</th>
                  <th>操作</th>
              </tr>
              <c:forEach var="entry" items="${requestScope.map}">
                  <tr>
                      <td>${entry.value}</td>    
                      <td>
                          <c:url var="myURL" value="/DownloadServlet">
                              <c:param name="uuidFileName" value="${entry.key}"/>
                          </c:url>
                          <a  href="${myURL}" 
                              style="text-decoration:none">
                              下载
                          </a>
                      </td>
                  </tr>
              </c:forEach>
          </table>
      </body>
    </html>
    package cn.itcast.web.servlet.download;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.URLEncoder;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import cn.itcast.web.servlet.util.UploadUtil;
    
    //下载文件到本地
    public class DownloadServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            String uuidFileName = request.getParameter("uuidFileName");
            byte[] buf = uuidFileName.getBytes("ISO8859-1");
            uuidFileName = new String(buf,"UTF-8");
            int index = uuidFileName.lastIndexOf("_");
            String realFileName = uuidFileName.substring(index+1);
            response.setHeader("content-disposition","attachment;filename="+URLEncoder.encode(realFileName,"UTF-8"));
            String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
            String uuidFilePath = UploadUtil.makeUuidFilePath(uploadPath,uuidFileName);
            InputStream is = new FileInputStream(uuidFilePath+"/"+uuidFileName);
            //模式:/WEB-INF/upload/12/4/43213_cc.jpg
            OutputStream os = response.getOutputStream(); 
            buf = new byte[1024];
            int len = 0;
            while((len=is.read(buf))>0){
                os.write(buf,0,len);
            }
            is.close();
            os.close();
        }
    }

    *1 文件上传下载和数据库结合
      1)速度:较小文件的文件存入数据库中,取出速度较快,返之较慢。
               较大文件的存入硬盘中,取出速度相对于数据库较快。
      2)同步:
        数据库表和硬盘必须一致,必须要事务的支持
            在事务的情况下,表操作优先,硬盘其后

    package cn.itcast.web.servlet.domain;
    
    public class Up {
         private int id;
         private String username;
         private String realFileName;
         private String uuidFileName;
         public Up(){}
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public String getRealFileName() {
            return realFileName;
        }
        public void setRealFileName(String realFileName) {
            this.realFileName = realFileName;
        }
        public String getUuidFileName() {
            return uuidFileName;
        }
        public void setUuidFileName(String uuidFileName) {
            this.uuidFileName = uuidFileName;
        }
    }
    package cn.itcast.web.servlet.dao;
    
    import java.sql.SQLException;
    import org.apache.commons.dbutils.QueryRunner;
    import cn.itcast.web.servlet.domain.Up;
    import cn.itcast.web.servlet.util.JdbcUtil;
    
    public class UpDao {
        //增加Up对象
        public void addUp(Up up) throws SQLException{
            QueryRunner runner = new QueryRunner(JdbcUtil.getDataSource());
            String sql = "insert into up(username,realFileName,uuidFileName) values(?,?,?)";
            runner.update(sql,new Object[]{up.getUsername(),up.getRealFileName(),up.getUuidFileName()});
        }
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <c3p0-config>
        <default-config>
            <property name="driverClass">com.mysql.jdbc.Driver</property>
            <property name="user">root</property>
            <property name="password">root</property>
            <property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/mydb3</property>
        </default-config>
    </c3p0-config>
    package cn.itcast.web.servlet.util;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    //JDBC工具类:关闭流和取得连接
    public final class JdbcUtil {
        private static ComboPooledDataSource dataSource;
        static{
            dataSource = new ComboPooledDataSource();
        }
        //取得数据源
        public static ComboPooledDataSource getDataSource() {
            return dataSource;
        }
        //取得连接
        public static Connection getMySqlConnection() throws SQLException{
            return  dataSource.getConnection();
        }
        //关闭连接
        public static void close(Connection conn) throws SQLException{
            if(conn!=null){
                conn.close();
            }
        }
        public static void close(PreparedStatement pstmt) throws SQLException {
            if(pstmt!=null){
                pstmt.close();
            }
        }
        public static void close(ResultSet rs) throws SQLException {
            if(rs!=null){
                rs.close();
            }
        }
    
    }
    package cn.itcast.web.servlet.upload;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import cn.itcast.web.servlet.domain.Up;
    import cn.itcast.web.servlet.domain.User;
    import cn.itcast.web.servlet.exception.NoUpfileException;
    import cn.itcast.web.servlet.exception.UpfileSizeException;
    import cn.itcast.web.servlet.exception.UpfileTypeException;
    import cn.itcast.web.servlet.service.UpService;
    import cn.itcast.web.servlet.util.UploadUtil;
    
    public class UploadServlet extends HttpServlet {
        public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
            try {
                User user = UploadUtil.doUpload(request);
                String uploadPath = this.getServletContext().getRealPath(UploadUtil.uploadPath);
                
                List<Up> upList = new ArrayList<Up>();
                
                //写入硬盘
                UploadUtil.doSave(user,uploadPath,upList);
                
                //写入数据库表
                UpService upService = new UpService();
                upService.addUps(upList);
                
                request.setAttribute("message","上传文件成功");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(UpfileSizeException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='green'>上传文件大小限制在200K以内</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(UpfileTypeException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='red'>只能上传JPG格式的文件</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch(NoUpfileException e){
                e.printStackTrace();
                request.setAttribute("message","<font color='blue'>无上传文件</font>");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }catch (Exception e) {
                e.printStackTrace();
                request.setAttribute("message","上传文件失败");
                request.getRequestDispatcher("/WEB-INF/message.jsp").forward(request,response);
            }
        }
    }
  • 相关阅读:
    Vue项目中全局过滤器的使用(格式化时间)
    vue-photo-preview 图片放大功能
    mongoimport导入json文件
    node后台,MongoDB作为数据库,vue前端获取数据并渲染
    JeasyUI,导出Excel
    EasyUI的textbox的disable ,readonly 用法
    EasyUI 中 Combobox里的onChange和onSelect事件的区别
    NullReferenceException 的可恨之处
    最新国家行政区划代码,来自国家统计局2018年底最新数据
    把旧系统迁移到.Net Core 2.0 日记 (20) --使用MiniProfiler for .NET
  • 原文地址:https://www.cnblogs.com/sunhan/p/3542135.html
Copyright © 2020-2023  润新知