码迷,mamicode.com
首页 > Web开发 > 详细

文件上传和下载--详解实现

时间:2017-11-25 14:27:06      阅读:1107      评论:0      收藏:0      [点我收藏+]

标签:leo   通过   none   str   表单   print   工具类   commons   传统   

 1.文件上传和下载分析

文件上传: 就是将客户端的数据发送到服务器上
文件上传要求:
            浏览器端要求:
                                    1.表单提交方式 post
                                    2.提供文件上传框(组件) input type="file"
                                    3.表单entype属性必须为 multipart/form-data
             服务器端要求:
                                    request.getInputStream()
             注意:
                            若表单使用了 multipart/form-data ,使用原生request.getParameter去获取参数的时候都为null ,请求头有请求类型context-type:multipart/form-data 

                           因此要用流接收  ServletInputStream servletInputStream=request.getInputStream();

                           技术分享图片

                             由于解析太过复杂,因此 使用工具类或者框架去解析用户上传的内容
                               commons-fileupload,struts(底层:commons-fileupload),serlvet3.0
                              apache出品的一款专门处理文件上传的工具类  commons-fileupload

              上传注意问题:
                                1.文件名称  浏览器不同,有可能获取的名称不同, 1.txt 或者 G:\1.txt 
                                2.文件重名  给文件来一个随机名称 uuid方式  毫米值+三个随机数
                                  数据库中可以存放多个字段: 文件名称和文件路径
                                 文件名称:身份证正面.jpg
                                 文件路径:g:\123123123423sfsf.jpg
                                 文件下载:  response.setHeader("content-disposition","attachment;filename="+真实名称);
                                3.文件安全(fastDFS)  安全目录:web-inf meta-inf 项目之外的目录
                                   不安全目录:项目目录(除去web-inf和meta-inf)
                               4.目录分离常用的方式:
                                  按用户划分、按时间划分、按数量划分、随机划分 
2.文件上传和下载实现 

              2.1、  创建一个fileUpload项目,加入Apache的commons-fileupload、commons-io文件上传组件的相关Jar包,如下图所示      技术分享图片技术分享图片

技术分享图片
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/fileServlet" method="post" enctype="multipart/form-data"> 
        <input type="file" name="file1"/>
        <input type="file" name="file2"/>
        <input type="text" name="username"/>
        <input type="submit" value="提交">
    </form>      
</body>
</html>
index.jsp 上传页面
技术分享图片
<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
  <head>
    <title>消息提示</title>
  </head>
  
  <body>
        ${message}
  </body>
</html>
message 上传成功返回的页面
技术分享图片
package com.zwj.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

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 fileServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
                    //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
                    System.out.println(this.getServletName()); 
                    System.out.println(this.getServletInfo());
                    String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
                    File file = new File(savePath);
                    //判断上传文件的保存目录是否存在
                    if (!file.exists() && !file.isDirectory()) {
                        System.out.println(savePath+"目录不存在,需要创建");
                        //创建目录
                        file.mkdir();
                    }
                    //消息提示
                    String message = "";
                    try{
                        //使用Apache文件上传组件处理文件上传步骤:
                        //1、创建一个DiskFileItemFactory工厂
                        DiskFileItemFactory factory = new DiskFileItemFactory();
                        //2、创建一个文件上传解析器
                        ServletFileUpload upload = new ServletFileUpload(factory);
                         //解决上传文件名的中文乱码
                        upload.setHeaderEncoding("UTF-8"); 
                        //3、判断提交上来的数据是否是上传表单的数据
                        if(!ServletFileUpload.isMultipartContent(request)){
                            //按照传统方式获取数据
                            return;
                        }
                        //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
                        List<FileItem> list = upload.parseRequest(request);
                        for(FileItem item : list){
                            //如果fileitem中封装的是普通输入项的数据
                            if(item.isFormField()){
                                String name = item.getFieldName();
                                //解决普通输入项的数据的中文乱码问题
                                String value = item.getString("UTF-8");
                                //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                                System.out.println(name + "=" + value);
                            }else{//如果fileitem中封装的是上传文件
                                //得到上传的文件名称,
                                String filename = item.getName();
                                System.out.println(filename);
                                if(filename==null || filename.trim().equals("")){
                                    continue;
                                }
                                //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
                                //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                                filename = filename.substring(filename.lastIndexOf("\\")+1);
                                //获取item中的上传文件的输入流
                                InputStream in = item.getInputStream();
                                //创建一个文件输出流
                                FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
                                //创建一个缓冲区
                                byte buffer[] = new byte[1024];
                                //判断输入流中的数据是否已经读完的标识
                                int len = 0;
                                //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                                while((len=in.read(buffer))>0){
                                    //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
                                    out.write(buffer, 0, len);
                                }
                                //关闭输入流
                                in.close();
                                //关闭输出流
                                out.close();
                                //删除处理文件上传时生成的临时文件
                                item.delete();
                                message = "文件上传成功!";
                            }
                        }
                    }catch (Exception e) {
                        message= "文件上传失败!";
                        e.printStackTrace();
                        
                    }
                    request.setAttribute("message",message);
                    request.getRequestDispatcher("/message.jsp").forward(request, response);
        }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         /*
          * 表单提交时如果没有加 enctype="multipart/form-data",可以接受,但只是name的属性值
          * 加上后,使用request.getParameter("username") 为 null;
          * 因此要用流接收
          */
         //String username=request.getParameter("username");
          //String filename=request.getParameter("filesname");
         //  ServletInputStream servletInputStream=request.getInputStream();
         // System.out.println(servletInputStream);
          //org.apache.catalina.connector.CoyoteInputStream@18906123
          this.doGet(request, response);
        
    }
}
fileServlet 上传功能控制类

   2.2、上述的代码虽然可以成功将文件上传到服务器上面的指定目录中,但是文件上传功能有许多需要注意的小细节问题,以下的几点特别注意的

  1、为保证服务器安全,上传文件应该放在外界无法直接访问的目录下,比如放于WEB-INF目录下。

  2、为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名。

  3、为防止一个目录下面出现太多文件,要使用hash算法打散存储。

  4、要限制上传文件的最大值。

  5、要限制上传文件的类型,在收到上传文件名时,判断后缀名是否合法。

  针对上述提出的5点细节问题,我们来改进一下UploadHandleServlet,改进后的代码如下:

技术分享图片
package com.zwj.demo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;


/**
 * 上传下载功能
 */
public class fileServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    @Override
      public void doGet(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
                 //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
                 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload");
                 //上传时生成的临时文件保存目录
                 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp");
                 File tmpFile = new File(tempPath);
                 if (!tmpFile.exists()) {
                     //创建临时目录
                     tmpFile.mkdir();
                 }
                 //消息提示
                 String message = "";
                 try{
                     //使用Apache文件上传组件处理文件上传步骤:
                     //1、创建一个DiskFileItemFactory工厂
                     DiskFileItemFactory factory = new DiskFileItemFactory();
                     //设置工厂的缓冲区的大小,当上传的文件大小超过缓冲区的大小时,就会生成一个临时文件存放到指定的临时目录当中。
                     factory.setSizeThreshold(1024*100);//设置缓冲区的大小为100KB,如果不指定,那么缓冲区的大小默认是10KB
                     //设置上传时生成的临时文件的保存目录
                     factory.setRepository(tmpFile);
                     //2、创建一个文件上传解析器
                     ServletFileUpload upload = new ServletFileUpload(factory);
                     //监听文件上传进度
                     upload.setProgressListener(new ProgressListener(){
                         public void update(long pBytesRead, long pContentLength, int arg2) {
                             System.out.println("文件大小为:" + pContentLength + ",当前已处理:" + pBytesRead);
                             /**
                              * 文件大小为:14608,当前已处理:4096
                                 文件大小为:14608,当前已处理:7367
                                 文件大小为:14608,当前已处理:11419
                                 文件大小为:14608,当前已处理:14608
                              */
                         }
                     });
                      //解决上传文件名的中文乱码
                     upload.setHeaderEncoding("UTF-8"); 
                     //3、判断提交上来的数据是否是上传表单的数据
                     if(!ServletFileUpload.isMultipartContent(request)){
                         //按照传统方式获取数据
                         return;
                     }
                     
                     //设置上传单个文件的大小的最大值,目前是设置为1024*1024字节,也就是1MB
                     upload.setFileSizeMax(1024*1024);
                     //设置上传文件总量的最大值,最大值=同时上传的多个文件的大小的最大值的和,目前设置为10MB
                     upload.setSizeMax(1024*1024*10);
                     //4、使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
                     List<FileItem> list = upload.parseRequest(request);
                     for(FileItem item : list){
                         //如果fileitem中封装的是普通输入项的数据
                         if(item.isFormField()){
                             String name = item.getFieldName();
                             //解决普通输入项的数据的中文乱码问题
                             String value = item.getString("UTF-8");
                             //value = new String(value.getBytes("iso8859-1"),"UTF-8");
                             System.out.println(name + "=" + value);
                         }else{//如果fileitem中封装的是上传文件
                             //得到上传的文件名称,
                             String filename = item.getName();
                             System.out.println(filename);
                             if(filename==null || filename.trim().equals("")){
                                 continue;
                             }
                             //注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,如:  c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt
                             //处理获取到的上传文件的文件名的路径部分,只保留文件名部分
                             filename = filename.substring(filename.lastIndexOf("\\")+1);
                             //得到上传文件的扩展名
                             String fileExtName = filename.substring(filename.lastIndexOf(".")+1);
                             //如果需要限制上传的文件类型,那么可以通过文件的扩展名来判断上传的文件类型是否合法
                             System.out.println("上传的文件的扩展名是:"+fileExtName);
                             //获取item中的上传文件的输入流
                             InputStream in = item.getInputStream();
                             //得到文件保存的名称
                             String saveFilename = makeFileName(filename);
                             //得到文件的保存目录
                             String realSavePath = makePath(saveFilename, savePath);
                             System.out.println(realSavePath + "\\" + saveFilename);
                             //创建一个文件输出流
                             FileOutputStream out = new FileOutputStream(realSavePath + "\\" + saveFilename);
                             //创建一个缓冲区
                             byte buffer[] = new byte[1024];
                             //判断输入流中的数据是否已经读完的标识
                             int len = 0;
                             //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
                             while((len=in.read(buffer))>0){
                                 //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
                                 out.write(buffer, 0, len);
                             }
                             //关闭输入流
                             in.close();
                             //关闭输出流
                             out.close();
                             //删除处理文件上传时生成的临时文件
                             //item.delete();
                             message = "文件上传成功!";
                         }
                     }
                 }catch (FileUploadBase.FileSizeLimitExceededException e) {
                     e.printStackTrace();
                     request.setAttribute("message", "单个文件超出最大值!!!");
                     request.getRequestDispatcher("/message.jsp").forward(request, response);
                     return;
                 }catch (FileUploadBase.SizeLimitExceededException e) {
                     e.printStackTrace();
                     request.setAttribute("message", "上传文件的总的大小超出限制的最大值!!!");
                     request.getRequestDispatcher("/message.jsp").forward(request, response);
                     return;
                 }catch (Exception e) {
                     message= "文件上传失败!";
                     e.printStackTrace();
                 }
                 request.setAttribute("message",message);
                 request.getRequestDispatcher("/message.jsp").forward(request, response);
     }
     
     /**
     * @Method: makeFileName
     * @Description: 生成上传文件的文件名,文件名以:uuid+"_"+文件的原始名称
     * @param filename 文件的原始名称
     * @return uuid+"_"+文件的原始名称
     */ 
     private String makeFileName(String filename){  //2.jpg
         //为防止文件覆盖的现象发生,要为上传文件产生一个唯一的文件名
         return UUID.randomUUID().toString() + "_" + filename;
     }
     
     /**
      * 为防止一个目录下面出现太多文件,要使用hash算法打散存储
     * @Method: makePath
     * @Description: 
     * @param filename 文件名,要根据文件名生成存储目录
     * @param savePath 文件存储路径
     * @return 新的存储目录
     */ 
     private String makePath(String filename,String savePath){
         //得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
         int hashcode = filename.hashCode();
         int dir1 = hashcode&0xf;  //0--15
         int dir2 = (hashcode&0xf0)>>4;  //0-15
         //构造新的保存目录
         String dir = savePath + "\\" + dir1 + "\\" + dir2;  //upload\2\3  upload\3\5
         //File既可以代表文件也可以代表目录
         File file = new File(dir);
         //如果目录不存在
         if(!file.exists()){
             //创建目录
             file.mkdirs();
         }
         return dir;
     }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         /*
          * 表单提交时如果没有加 enctype="multipart/form-data",可以接受,但只是name的属性值
          * 加上后,使用request.getParameter("username") 为 null;
          * 因此要用流接收
          */
         //String username=request.getParameter("username");
          //String filename=request.getParameter("filesname");
         //  ServletInputStream servletInputStream=request.getInputStream();
         // System.out.println(servletInputStream);
          //org.apache.catalina.connector.CoyoteInputStream@18906123
          this.doGet(request, response);
        
    }
}
/*文件大小为:87999,当前已处理:4096
文件大小为:87999,当前已处理:7600
文件大小为:87999,当前已处理:11652
文件大小为:87999,当前已处理:15200
文件大小为:87999,当前已处理:19252
文件大小为:87999,当前已处理:22800
文件大小为:87999,当前已处理:26852
文件大小为:87999,当前已处理:30400
文件大小为:87999,当前已处理:34452
文件大小为:87999,当前已处理:38000
文件大小为:87999,当前已处理:42052
文件大小为:87999,当前已处理:45600
文件大小为:87999,当前已处理:49652
文件大小为:87999,当前已处理:53200
文件大小为:87999,当前已处理:57252
文件大小为:87999,当前已处理:60800
文件大小为:87999,当前已处理:64852
文件大小为:87999,当前已处理:68400
文件大小为:87999,当前已处理:72452
文件大小为:87999,当前已处理:76000
文件大小为:87999,当前已处理:80052
文件大小为:87999,当前已处理:83600
文件大小为:87999,当前已处理:87652
文件大小为:87999,当前已处理:87999
下载必看.doc
上传的文件的扩展名是:doc
D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\fileUpload\WEB-INF\upload\10\1\bee18e49-8fad-434e-aad7-6c2e08284cfc_下载必看.doc
CookUtils.java
上传的文件的扩展名是:java
D:\workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\fileUpload\WEB-INF\upload\11\0\a0fab1db-9e30-4475-8f96-c42f4c8714ae_CookUtils.java
username=name
*/
fileServlet 上传功能修改后的

 

 

        

文件上传和下载--详解实现

标签:leo   通过   none   str   表单   print   工具类   commons   传统   

原文地址:http://www.cnblogs.com/ou-pc/p/7894986.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!