java文件上传技术深入剖析

时间:2022-01-21 10:13:38

本文实例为大家分享了java文件上传技术,供大家参考,具体内容如下

java文件上传技术深入剖析

表单:
客户端发送HTTP必须使用multipart/form-data数据类型,表示复合数据类型。即:
在表单中使用html标签。

需要的包:
        Commons-fileupload.jar,核心上传文件工具都在这个包中。
        commons-io.jar – 上传文件所需要的包

上传文件类详解:
DiskFileItemFactory-创建监时文件目录,指是缓存区大小
ServletFileUpload用于解析HttpServletRequest。返回一组文件对象。
FileItem – 表示用户上传的每一个文件对像。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
 
  <title>文件上传演示</title>
 </head>
 
 <body>
  <font color="red" size="6">过渡板--了解底层</font>
  <!-- multipart/form-data:多部分(不但有文件,也有部分) -->
  <form action="<%=request.getContextPath()%>/upload0" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="file"/>
    <input type="submit" value="上传"/>
    <!-- 上传的文件名不能为中文,否则获取的文件名是乱码,不过下面的例子可以解决这个问题 -->
  </form>
  <br/>
 
 
  <font color="red" size="6">使用apache文件上传工具实现文件上传</font>
  <!-- application/x-www-form-urlencoded -->
  <form action="<%=request.getContextPath()%>/upload" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="file"/>
    <input type="submit" value="上传"/> 
  </form>
 
 
  <font color="red" size="6">使用apache文件上传工具实现文件上传2(解决文件名乱码)</font>
  <p>
  POST1(普通表单):enctype=application/x-www-form-urlencoded(默认值)
  </p>
  <p>
  POST2(上传文件表单):enctype=multipart/form-data:多部分(不但有文件,也有部分)
  </p>
  <form action="<%=request.getContextPath()%>/upload2" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="file"/><!-- POST2(上传文件表单) --><br/>
    文件说明:<input type="text" name="desc"/><!-- POST1(普通表单) --><br/><br/>
    文件2:<input type="file" name="file"/><br/>
    文件说明2:<input type="text" name="desc"/>
    <input type="submit" value="上传"/> 
  </form>
  <font color="red" size="6">使用apache文件上传工具实现文件上传3(文件打散)</font>
  <!-- POST1(普通表单):enctype=application/x-www-form-urlencoded(默认值) -->
  <!-- POST2(上传文件表单):enctype=multipart/form-data:多部分(不但有文件,也有部分) -->
  <form action="<%=request.getContextPath()%>/upload3" method="post" enctype="multipart/form-data">
    文件:<input type="file" name="file"/><!-- POST2(上传文件表单) --><br/>
    文件说明:<input type="text" name="desc"/><!-- POST1(普通表单) --><br/><br/>
    文件2:<input type="file" name="file"/><br/>
    文件说明2:<input type="text" name="desc"/>
    <input type="submit" value="上传"/> 
  </form>
 </body>
</html>

java文件上传技术深入剖析

过渡板–了解底层

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package cn.hncu.servlet;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
public class Upload0Servlet extends HttpServlet {
 
 
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
    InputStream in=request.getInputStream();
    BufferedReader br=new BufferedReader(new InputStreamReader(in));
    String line;
    while((line=br.readLine())!=null){
      System.out.println(line);
    }
  }
 
}

java文件上传技术深入剖析

java文件上传技术深入剖析

使用apache文件上传工具实现文件上传

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package cn.hncu.servlet;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
 
public class UploadServlet extends HttpServlet {
 
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
    out.println("<HTML>");
    out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
    out.println(" <BODY>");
    out.print("不支持Get方式上传。。。。。。");
    out.println(" </BODY>");
    out.println("</HTML>");
    out.flush();
    out.close();
  }
 
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
        //在服务器上,为所有上传文件指定一个存放目录
    String path=getServletContext().getRealPath("/upload");
    System.out.println("path:"+path);
    File dir=new File(path);
    if(!dir.exists()){
      dir.mkdirs();
    }
 
    //创建一个基于硬盘的工厂
    //DiskFileItemFactory disk = new DiskFileItemFactory();
    //设置临时目录(建议设计临时目录,否则会使用系统临时目录。)
    //disk.setRepository(new File(“d:/a”));
    //3、 设置向硬盘写数据的缓冲区大小
disk.setSizeThreshold(1024*4);//当文件大于此设置时,将会在临时目录下形成临时文件
 
 
    //设置临时文件缓冲区大小--8K缓冲,临时文件地址
    DiskFileItemFactory f=new DiskFileItemFactory(1024*8, new File("d:/a"));
    //上传工具--创建用于解析的对像
    ServletFileUpload upload=new ServletFileUpload(f);
    upload.setFileSizeMax(1024*1024*5);//设置上传的单个文件最大为5M
    //设置上传文件的最大大小,如果是多个文件,则为多个文件的和最大8M
    upload.setSizeMax(1024*1024*8);//设置所有上传的文件大小之和最大为8M
    //使用解析工具解析
    try {
      List<FileItem> list=upload.parseRequest(request);
      for(FileItem fI:list){
        System.out.println("文件内容类型:"+fI.getContentType());//文件内容类型:text/plain
        System.out.println("文件名:"+fI.getName());//文件名:C:\Users\adl1\Desktop\a.txt
        String ext=fI.getName().substring(fI.getName().lastIndexOf("."));//.txt
        String uuid=UUID.randomUUID().toString().replace("-", "");
        String fileName=uuid+ext;
//       FileUtils.copyInputStreamToFile(fI.getInputStream(), new File("d:/a/d/a.txt"));//写死了
        //fI.getInputStream()是真正文件信息
        FileUtils.copyInputStreamToFile(fI.getInputStream(), new File(path+"/"+fileName));//写活了
      }
    } catch (FileUploadException e) {
      e.printStackTrace();
    }
 
 
  }
 
}

在这个地方存储上传的文件

java文件上传技术深入剖析

上传信息:

java文件上传技术深入剖析

上传结果:

java文件上传技术深入剖析

使用apache文件上传工具实现文件上传2(解决文件名乱码)

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package cn.hncu.servlet;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
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.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
 
public class Upload2Servlet extends HttpServlet {
 
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
 
    response.setContentType("text/html");
    //如果是含上传文件的表单(POST2),该剧只能设置所上传文件的文件名中的编码(解决他的中文乱码)
    //但不能解决在POST2方式下的普通表单组件的中文乱码
    PrintWriter out = response.getWriter();
    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
    out.println("<HTML>");
    out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
    out.println(" <BODY>");
    out.print("不支持Get方式上传。。。。。。");
    out.println(" </BODY>");
    out.println("</HTML>");
    out.flush();
    out.close();
  }
 
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
    //第一步
    //普通的form表单(POST1),下面这句可以设置普通表单组件内容的编码(能够解决它们的中文乱码问题)
    request.setCharacterEncoding("utf-8");
    //如果是含上传文件的表单(POST2),该句只能设置所上传文件的文件名中的编码(解决它的中文乱码)。但不能解决普通表单组件的乱码(不能设它编码)
 
 
    //在服务器上,为所有上传文件指定一个存放目录
    String path=getServletContext().getRealPath("/upload");
    System.out.println("path:"+path);
    File dir=new File(path);
    if(!dir.exists()){
      dir.mkdirs();
    }
 
 
    //设置临时文件缓冲区大小--8K缓冲,临时文件地址
    DiskFileItemFactory f=new DiskFileItemFactory(1024*8, new File("d:/a"));
    //上传工具
    ServletFileUpload upload=new ServletFileUpload(f);
    upload.setFileSizeMax(1024*1024*5);//设置上传的单个文件最大为5M
    upload.setSizeMax(1024*1024*8);//设置所有上传的文件大小之和最大为8M
    //使用解析工具解析
    try {
      List<FileItem> list=upload.parseRequest(request);
      for(FileItem fI:list){
        if((fI.isFormField())){//如果是普通表单组件:checkbox,radio,password...
//         String desc=fI.getString();
          System.out.println("fI.getString():"+fI.getString());
          //第二步
          String desc=fI.getString("utf-8");
          //该句设置普通表单组件内容编码
          System.out.println("编码后:"+desc);
        }else{
          String ext=fI.getName().substring(fI.getName().lastIndexOf("."));//.txt
          String uuid=UUID.randomUUID().toString().replace("-", "");
          String fileName=uuid+ext;
          //fI.getInputStream()是真正文件信息
          FileUtils.copyInputStreamToFile(fI.getInputStream(), new File(path+"/"+fileName));//写活了
        }
      }
    } catch (FileUploadException e) {
      e.printStackTrace();
    }
  }
}

上传信息:

java文件上传技术深入剖析

上传结果:


java文件上传技术深入剖析

使用apache文件上传工具实现文件上传3(文件打散)

用Hash目录优化文件存储:
Hash目录是一种优化文件存储性能的方法。无论是Windows还是Linux,无论是NTFS还是ext3,每个目录下所能容纳的项目数是有限的。
并不是不能保存,而是当项目数量过大的时候,会降低文件索引速度,
所以权衡一个目录下应该保存多少文件是很必要的。保存得多了会影响性能,保存得少了会造成目录太多和空间浪费。所以当保存大批文件的时候,
需要有一种算法能将文件比较均匀地“打散”在不同的子目录下以提高每一级的索引速度,这种算法就是 Hash。通常用的MD5、sha1等都可以用来做Hash目录,我的Session里也同样使用了MD5,取得sessionID的第一位和第九位,这就构成了两级Hash路径,也就是说,系统把所有的Session文件分散到了16×16=256个子目录下。假设Linux每个目录下保存1000个文件可以获得最好的空间性能比,那么系统在理想情况下可以同时有256000个session文件在被使用。

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package cn.hncu.servlet;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
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.FileUploadException;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
 
public class Upload3Servlet extends HttpServlet {
 
  public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
 
    response.setContentType("text/html");
    //如果是含上传文件的表单(POST2),该剧只能设置所上传文件的文件名中的编码(解决他的中文乱码)
    //但不能解决在POST2方式下的普通表单组件的中文乱码
    PrintWriter out = response.getWriter();
    out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
    out.println("<HTML>");
    out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
    out.println(" <BODY>");
    //获取GET方式的url中“?”号后面的部分
    //http://localhost:8080/servletDemo3/upload?name=Jack&sex=male
    String qStr = request.getQueryString();
    System.out.println("qStr: "+qStr);//qStr: name=Jack&sex=male
    out.print("不支持Get方式上传。。。。。。");
    out.println(" </BODY>");
    out.println("</HTML>");
    out.flush();
    out.close();
  }
 
  public void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
 
    response.setContentType("text/html;charset=utf-8");
    PrintWriter out = response.getWriter();
    //1防黑: 防护前端采用POST1方式提交
    //法1
    /*
    String type=request.getContentType();
    if(!type.contains("multipart/form-data")){
      out.println("不支持普通表单提交");
      return;
    }*/
    //法2
    boolean boo = ServletFileUpload.isMultipartContent(request);
    if(!boo){
      out.println("不支持普通表单提交");
      return;
    }
 
    //第一步
    //普通的form表单(POST1),下面这句可以设置普通表单组件内容的编码(能够解决它们的中文乱码问题)
    request.setCharacterEncoding("utf-8");
    //如果是含上传文件的表单(POST2),该句只能设置所上传文件的文件名中的编码(解决它的中文乱码)。但不能解决普通表单组件的乱码(不能设它编码)
 
 
    //在服务器上,为所有上传文件指定一个存放目录
    String path=getServletContext().getRealPath("/upload");
    System.out.println("path:"+path);
    File dir=new File(path);
    if(!dir.exists()){
      dir.mkdirs();
    }
 
 
    //设置临时文件缓冲区大小--8K缓冲,临时文件地址
    DiskFileItemFactory f=new DiskFileItemFactory(1024*8, new File("d:/a"));
    //上传工具
    ServletFileUpload upload=new ServletFileUpload(f);
    upload.setFileSizeMax(1024*1024*5);//设置上传的单个文件最大为5M
    upload.setSizeMax(1024*1024*8);//设置所有上传的文件大小之和最大为8M
 
    //▲4上传进度监听
    upload.setProgressListener(new ProgressListener(){
      private double pre=0D;
      @Override//参数1:已上传多少字节 参数2:一共多少字节  参数3:第几个文件(序号从1开始)
      public void update(long pByteRead, long pContentLength, int pItems) {
        double d = 1.0*pByteRead/pContentLength*100;
        System.out.println(d+"%");
            if(pre!=d){
              System.out.println(d+"%");
              pre=d;
            }
      }
    });
 
 
    //使用解析工具解析
    try {
      List<FileItem> list=upload.parseRequest(request);
      for(FileItem fI:list){
        if((fI.isFormField())){//如果是普通表单组件:checkbox,radio,password...
//         String desc=fI.getString();
          System.out.println("fI.getString():"+fI.getString());
          //第二步
          String desc=fI.getString("utf-8");
          //该句设置普通表单组件内容编码
          System.out.println("编码后:"+desc);
        }else{
          //防护:过滤掉没选择文件的空文件组件
          if(fI.getSize()<=0){
            continue;//读下一个文件
          }
 
          System.out.println("文件内容类型:"+fI.getContentType());//文件内容类型:text/plain
          System.out.println("文件名:"+fI.getName());//文件名:C:\Users\adl1\Desktop\a.txt
 
          String ext=fI.getName().substring(fI.getName().lastIndexOf("."));//.txt
          String uuid=UUID.randomUUID().toString().replace("-", "");
          String fileName=uuid+ext;
 
          //文件目录打散技术
          String dir1=Integer.toHexString(uuid.hashCode()&0x0f);
          String dir2=Integer.toHexString((uuid.hashCode()&0xf0)>>4);
 
 
          //fI.getInputStream()是真正文件信息
          FileUtils.copyInputStreamToFile(fI.getInputStream(), new File(path+"/"+dir1+"/"+dir2+"/"+fileName));//写活了
        }
      }
    } catch (FileUploadException e) {
      e.printStackTrace();
    }
  }
}

打散信息:

java文件上传技术深入剖析

打散结果:

文件1:

java文件上传技术深入剖析

文件2:

java文件上传技术深入剖析

演示上传进度原理

java文件上传技术深入剖析

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。