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

 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);
          //[email protected]
          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);
          //[email protected]
          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 上传功能修改后的

时间: 2024-10-10 04:09:52

文件上传和下载--详解实现的相关文章

Django文件上传机制用法详解(转)

Django文件上传机制用法详解 http://www.jbxue.com/article/24283.html 分享下Django文件上传机制的用法,包括基本上传文件的原理,以及如何处理上传文件的方法,需要的朋友参考下. 当Django处理上传一个文件时,文件数据被放在request.FILES中. 这个文档解释文件怎么样被存储在磁盘上或者内存中,怎样定制默认的行为. 一,基本文件上传考虑一个包含FileField的简单的表单: 复制代码 代码示例: from  django  import 

jquery的uploadify插件多文件上传配置参数详解

最近做了个多文件上传,需要限制上传文件类型的例子.以前没做过找了一些资料,下次有用.同时也给大家做参考. uploader: uploadify.swf 文件的相对路径,该swf文件是一个带有文字BROWSE的按钮,点击后淡出打开文件对话框,默认值:uploadify.swf. script: 后台处理程序的相对路径 .默认值:uploadify.php checkScript:用来判断上传选择的文 件在服务器是否存在的后台处理程序的相对路径 fileDataName:设置一个名字,在服务器处理

文件上传插件uploadify详解

官网:http://www.uploadify.com/ 基于jquery的文件上传控件,支持ajax无刷新上传,多个文件同时上传,上传进行进度显示,删除已上传文件. 要求使用jquery1.4或以上版本,flash player 9.0.24以上. 有两个版本,一个用flash,一个是html5.html5的需要付费~所以这里只说flash版本的用法.

express文件上传中间件Multer详解

Express默认并不处理HTTP请求体中的数据,对于普通请求体(JSON.二进制.字符串)数据,可以使用body-parser中间件.而文件上传(multipart/form-data请求),可以基于请求流处理,也可以使用formidable模块或Multer中间件. 1. multer中间件 Multer是Express官方推出的,用于Node.jsmultipart/form-data请求数据处理的中间件. 它基于busboy构建,可以高效的处理文件上传,但并不处理multipart/fo

java里面的文件上传与下载

文件的上传与下载主要用到两种方法:1.方法一:commons-fileupload.jar commons-io.jarapache的commons-fileupload实现文件上传,下载 [upload]package com.handson.bbs.servlet; import java.io.File;import java.io.IOException;import java.util.Date;import java.util.List;import javax.servlet.Se

JavaWeb学习总结(五十)——文件上传和下载

在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,stru

深入分析JavaWeb Item40 -- 文件上传和下载

在Web应用系统开发中,文件上传和下载功能是非常常用的功能,今天来讲一下JavaWeb中的文件上传和下载功能的实现. 对于文件上传,浏览器在上传的过程中是将文件以流的形式提交到服务器端的,如果直接使用Servlet获取上传文件的输入流然后再解析里面的请求参数是比较麻烦,所以一般选择采用apache的开源工具common-fileupload这个文件上传组件.这个common-fileupload上传组件的jar包可以去apache官网上面下载,也可以在struts的lib文件夹下面找到,stru

文件上传与下载!

1.上传: 1.上传数据的类型:字符,字节 1.文本类型(字符):通过url网址的?:通过表单元素:AJAX. 2.文件类型(字节):通过表单元素(file). 2.上传文件的方式: 1.form表单实现文件上传:常用方式(上传文件用post,不用get,因为get方式对于上传量有限) 通过鼠标单击,在File标签中选择的文件,才能上传. 2.通过AJAX实现文件上传:禁用方式   AJAX的数据传递通过javascript脚本取值.如果传递文件,那么也需要通过javascript脚本获取文件内

springmvc和servlet下的文件上传和下载(存文件目录和存数据库Blob两种方式)

项目中涉及了文件的上传和下载,以前在struts2下做过,今天又用springmvc做了一遍,发现springmvc封装的特别好,基本不用几行代码就完成了,下面把代码贴出来: FileUpAndDown.jsp <%@ page language="java" contentType="text/html; charset=UTF-8"%> <html> <head> <title>using commons Uplo