一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語言 - JAVA教程 - JavaEE組件commons-fileupload實(shí)現(xiàn)文件上傳、下載

JavaEE組件commons-fileupload實(shí)現(xiàn)文件上傳、下載

2020-06-23 11:47rocomp JAVA教程

這篇文章主要介紹了JavaEE組件commons-fileupload實(shí)現(xiàn)文件上傳、下載,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

一、文件上傳概述

實(shí)現(xiàn)Web開發(fā)中的文件上傳功能,需要兩步操作:

1、在Web頁面中添加上傳輸入項(xiàng)

?
1
2
3
4
5
6
7
8
9
10
<form action="#" method="post" enctype="multipart/form-data">
  <input type="file" name="filename1"/><br>
  <input type="file" name="filename2"/><br>
  <input type="submit" value="上傳"/>
<form>
<!-- 1、表單方式必須是post
  2、必須設(shè)置encType屬性為 multipart/form-data.設(shè)置該值后,瀏覽器在上傳文件時,將會把文件數(shù)據(jù)附帶在http請求消息體中,
    并使用MIME協(xié)議對上傳的文件進(jìn)行描述,以方便接收方對上傳數(shù)據(jù)進(jìn)行解析和處理。
  3、必須要設(shè)置input的name屬性,否則瀏覽器將不會發(fā)送上傳文件的數(shù)據(jù)。
-->

2、在Servlet中讀取文件上傳數(shù)據(jù),并保存到服務(wù)器硬盤

Request對象提供了一個getInputStream方法,通過這個方法可以讀取到客戶端提交過來的數(shù)據(jù)。但由于用戶可能會同時上傳多個文件,在Servlet端編程直接讀取上傳數(shù)據(jù),并分別解析出相應(yīng)的文件數(shù)據(jù)是一項(xiàng)非常麻煩的工作。

比如下面是截取的瀏覽器上傳文件時發(fā)送的請求的HTTP協(xié)議中的部分內(nèi)容:        

?
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
Accept-Language: zh-Hans-CN,zh-Hans;q=0.5
Content-Type: multipart/form-data; boundary=---------------------------7dfa01d1908a4
UA-CPU: AMD64
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.2; Win64; x64; Trident/7.0; rv:11.0) like Gecko
Content-Length: 653
Host: localhost:8080
Connection: Keep-Alive
Pragma: no-cache
Cookie: JSESSIONID=11CEFF8E271AB62CE676B5A87B746B5F
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="username"
zhangsan
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="userpass"
1234
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload.txt"
Content-Type: text/plain
this is first file content!
-----------------------------7dfa01d1908a4
Content-Disposition: form-data; name="filename1"; filename="C:\Users\ASUS\Desktop\upload2.txt"
Content-Type: text/plain
this is Second file content!
hello
-----------------------------7dfa01d1908a4--

 從上面的數(shù)據(jù)中也可以看出,如果自己手工的去分割讀取數(shù)據(jù)很難寫出健壯穩(wěn)定的程序。所以,為方便用戶處理上傳數(shù)據(jù),Apache開源組織提供了一個用來處理表單文件上傳的一個開源組件(Commons-fileupload),該組件性能優(yōu)異,并且其API使用極其簡單,可以讓開發(fā)人員輕松實(shí)現(xiàn)web文件上傳功能,因此在web開發(fā)中實(shí)現(xiàn)文件上傳功能,通常使用Commons-fileupload組件實(shí)現(xiàn)。

 需要導(dǎo)入兩個jar包:Commons-fileupload、commons-io

?
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
response.setContentType("text/html;charset=utf-8");//設(shè)置響應(yīng)編碼
    request.setCharacterEncoding("utf-8");
    PrintWriter writer = response.getWriter();//獲取響應(yīng)輸出流
    
    ServletInputStream inputStream = request.getInputStream();//獲取請求輸入流
    
    /*
     * 1、創(chuàng)建DiskFileItemFactory對象,設(shè)置緩沖區(qū)大小和臨時文件目錄
     *  該類有兩個構(gòu)造方法一個是無參的構(gòu)造方法,
     *  另一個是帶兩個參數(shù)的構(gòu)造方法
     * @param int sizeThreshold,該參數(shù)設(shè)置內(nèi)存緩沖區(qū)的大小,默認(rèn)值為10K。當(dāng)上傳文件大于緩沖區(qū)大小時,fileupload組件將使用臨時文件緩存上傳文件
     * @param java.io.File repository,該參數(shù)指定臨時文件目錄,默認(rèn)值為System.getProperty("java.io.tmpdir");
     *
     *  如果使用了無參的構(gòu)造方法,則使用setSizeThreshold(int sizeThreshold),setRepository(java.io.File repository)
     *  方法手動進(jìn)行設(shè)置
     */
    DiskFileItemFactory factory = new DiskFileItemFactory();
    
    int sizeThreshold=1024*1024;
    factory.setSizeThreshold(sizeThreshold);
    
    File repository = new File(request.getSession().getServletContext().getRealPath("temp"));
//    System.out.println(request.getSession().getServletContext().getRealPath("temp"));
//    System.out.println(request.getRealPath("temp"));
    factory.setRepository(repository);
    
    /*
     * 2、使用DiskFileItemFactory對象創(chuàng)建ServletFileUpload對象,并設(shè)置上傳文件的大小
     
     *  ServletFileUpload對象負(fù)責(zé)處理上傳的文件數(shù)據(jù),并將表單中每個輸入項(xiàng)封裝成一個FileItem
     *  該對象的常用方法有:
     *      boolean isMultipartContent(request);判斷上傳表單是否為multipart/form-data類型
     *      List parseRequest(request);解析request對象,并把表單中的每一個輸入項(xiàng)包裝成一個fileItem 對象,并返回一個保存了所有FileItem的list集合
     *      void setFileSizeMax(long filesizeMax);設(shè)置單個上傳文件的最大值
     *      void setSizeMax(long sizeMax);設(shè)置上傳溫江總量的最大值
     *      void setHeaderEncoding();設(shè)置編碼格式,解決上傳文件名亂碼問題
     */
    ServletFileUpload upload = new ServletFileUpload(factory);
    
    upload.setHeaderEncoding("utf-8");//設(shè)置編碼格式,解決上傳文件名亂碼問題
    /*
     * 3、調(diào)用ServletFileUpload.parseRequest方法解析request對象,得到一個保存了所有上傳內(nèi)容的List對象
     */
    List<FileItem> parseRequest=null;
    try {
       parseRequest = upload.parseRequest(request);
    } catch (FileUploadException e) {
      e.printStackTrace();
    }
    /*
     * 4、對list進(jìn)行迭代,每迭代一個FileItem對象,調(diào)用其isFormField方法判斷是否是文件上傳
     *  true表示是普通表單字段,則調(diào)用getFieldName、getString方法得到字段名和字段值
     *  false為上傳文件,則調(diào)用getInputStream方法得到數(shù)據(jù)輸入流,從而讀取上傳數(shù)據(jù)
     
     *  FileItem用來表示文件上傳表單中的一個上傳文件對象或者普通的表單對象
     *  該對象常用方法有:
     *     boolean isFormField();判斷FileItem是一個文件上傳對象還是普通表單對象
     *     true表示是普通表單字段,
     *         則調(diào)用getFieldName、getString方法得到字段名和字段值
     *     false為上傳文件,
     *         則調(diào)用getName()獲得上傳文件的文件名,注意:有些瀏覽器會攜帶客戶端路徑,需要自己減除
     *         調(diào)用getInputStream()方法得到數(shù)據(jù)輸入流,從而讀取上傳數(shù)據(jù)
     *         delete(); 表示在關(guān)閉FileItem輸入流后,刪除臨時文件。
     */
    
    for (FileItem fileItem : parseRequest) {
      if (fileItem.isFormField()) {//表示普通字段
        if ("username".equals(fileItem.getFieldName())) {
          String username = fileItem.getString();
          writer.write("您的用戶名:"+username+"<br>");
        }
        if ("userpass".equals(fileItem.getFieldName())) {
          String userpass = fileItem.getString();
          writer.write("您的密碼:"+userpass+"<br>");
        }
        
      }else {//表示是上傳的文件
        //不同瀏覽器上傳的文件可能帶有路徑名,需要自己切割
        String clientName = fileItem.getName();
        String filename = "";
        if (clientName.contains("\\")) {//如果包含"\"表示是一個帶路徑的名字,則截取最后的文件名
          filename = clientName.substring(clientName.lastIndexOf("\\")).substring(1);
        }else {
          filename = clientName;
        }
        
        UUID randomUUID = UUID.randomUUID();//生成一個128位長的全球唯一標(biāo)識
        
        filename = randomUUID.toString()+filename;
        
        /*
         * 設(shè)計(jì)一個目錄生成算法,如果所用用戶上傳的文件總數(shù)是億數(shù)量級的或更多,放在同一個目錄下回導(dǎo)致文件索引非常慢,
         * 所以,設(shè)計(jì)一個目錄結(jié)構(gòu)來分散存放文件是非常有必要,且合理的
         * 將UUID取哈希算法,散列到更小的范圍,
         * 將UUID的hashcode轉(zhuǎn)換為一個8位的8進(jìn)制字符串,
         * 從這個字符串的第一位開始,每一個字符代表一級目錄,這樣就構(gòu)建了一個八級目錄,每一級目錄中最多有16個子目錄
         * 這無論對于服務(wù)器還是操作系統(tǒng)都是非常高效的目錄結(jié)構(gòu)
         */
        int hashUUID =randomUUID.hashCode();
        String hexUUID = Integer.toHexString(hashUUID);
        //System.out.println(hexUUID);
        //獲取將上傳的文件存存儲在哪個文件夾下的絕對路徑
        String filepath=request.getSession().getServletContext().getRealPath("upload");
        for (char c : hexUUID.toCharArray()) {
          filepath = filepath+"/"+c;
        }
        //如果目錄不存在就生成八級目錄
        File filepathFile = new File(filepath);
        if (!filepathFile.exists()) {
          filepathFile.mkdirs();
        }
        //從Request輸入流中讀取文件,并寫入到服務(wù)器
        InputStream inputStream2 = fileItem.getInputStream();
        //在服務(wù)器端創(chuàng)建文件
        File file = new File(filepath+"/"+filename);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        
        byte[] buffer = new byte[10*1024];
        int len = 0;
        while ((len= inputStream2.read(buffer, 0, 10*1024))!=-1) {
          bos.write(buffer, 0, len);
        }
        writer.write("您上傳文件"+clientName+"成功<br>");
        //關(guān)閉資源
        bos.close();
        inputStream2.close();
      }
    }
  //注意Eclipse的上傳的文件是保存在項(xiàng)目的運(yùn)行目錄,而不是workspace中的工程目錄里。

 二、文件上傳需要特別注意的問題: (這些問題在上面的代碼中都提供了簡單的解決)

1、文件存放的位置

為保證服務(wù)器的安全,上傳文件應(yīng)保存在應(yīng)用程序的WEB-INF目錄下,或者不受WEB服務(wù)器管理的目錄,如果用戶上傳一個帶有可執(zhí)行代碼的文件,如jsp文件,根據(jù)拼接訪問路徑去訪問的話,可以在服務(wù)器端做任何事情。

2、為防止多用戶上傳形同文件名的文件,而導(dǎo)致文件覆蓋的情況發(fā)生,文件上傳程序應(yīng)保證上傳文件具有唯一文件名

使用UUID + 用戶上傳文件名的方式重命名

關(guān)于UUID:
UUID(Universally Unique Identifier)全局唯一標(biāo)識符,是指在一臺機(jī)器上生成的數(shù)字,它保證對在同一時空中的所有機(jī)器都是唯一的。按照開放軟件基金會(OSF)制定的標(biāo)準(zhǔn)計(jì)算,用到了以太網(wǎng)卡地址、納秒級時間、芯片ID碼和許多可能的數(shù)字。由以下幾部分的組合:當(dāng)前日期和時間(UUID的第一個部分與時間有關(guān),如果你在生成一個UUID之后,過幾秒又生成一個UUID,則第一個部分不同,其余相同),時鐘序列,全局唯一的IEEE機(jī)器識別號(如果有網(wǎng)卡,從網(wǎng)卡獲得,沒有網(wǎng)卡以其他方式獲得),UUID的唯一缺陷在于生成的結(jié)果串會比較長。

是一個128位長的數(shù)字,一般用16進(jìn)制表示。算法的核心思想是結(jié)合機(jī)器的網(wǎng)卡、當(dāng)?shù)貢r間、一個隨即數(shù)來生成GUID。從理論上講,如果一臺機(jī)器每秒產(chǎn)生10000000個GUID,則可以保證(概率意義上)3240年不重復(fù)。

從JDK1.5開始,生成UUID變成了一件簡單的事,以為JDK實(shí)現(xiàn)了UUID:

java.util.UUID,直接調(diào)用即可.
UUID uuid  =  UUID.randomUUID();
String s = UUID.randomUUID().toString();//用來生成數(shù)據(jù)庫的主鍵id非常不錯。。 
 
UUID是由一個十六位的數(shù)字組成,表現(xiàn)出來的形式例如
550E8400-E29B-11D4-A716-446655440000 

3、為防止單個目錄下文件過多,影響文件讀寫速度,處理上傳文件的程序應(yīng)該應(yīng)根據(jù)可能的上傳總量,選擇合適的目錄結(jié)構(gòu)生成算法,將上傳文件分散存儲。如使用hashcode方法構(gòu)建多級目錄。

4、如果不同用戶都上傳了相同的文件,那么在服務(wù)器端沒有必要存儲同一個文件的很多分拷貝,這樣很浪費(fèi)資源,應(yīng)該設(shè)計(jì)算法解決這種重復(fù)文件的問題。

5、JSP技術(shù)原理自動實(shí)現(xiàn)了多線程。所以開發(fā)者不需要考慮上傳文件的多線程操作 

三、文件下載 

?
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
<%
    ArrayList<String> fileNames = new ArrayList<String>();
    fileNames.add("file/aa.txt");
    fileNames.add("file/bb.jpg");
    for(String fileName : fileNames) {
   %>
   
    <form action="DownloadServlet" method="get">
      <input type="hidden" name="fileName" value="<%=fileName %>" />
      <input type="submit" value="下載:<%=fileName %>" />
    </form>
   <%
    }
   %>
 
    request.setCharacterEncoding("utf-8");
    
    String filename = request.getParameter("fileName");
    
    
    String urlname = URLEncoder.encode(filename, "utf-8");//防止文件名中有中文亂碼
    response.setHeader("Content-Disposition","attachment;filename="+urlname);
    
    FileInputStream fis = new FileInputStream(new File(request.getSession().getServletContext().getRealPath(filename)));
    BufferedInputStream bis = new BufferedInputStream(fis);
    ServletOutputStream sos = response.getOutputStream();
    
    byte[] buffer = new byte[1024];
    int len=0;
    while((len=bis.read(buffer, 0, 1024))!=-1){
      sos.write(buffer, 0, len);
    }
    bis.close();
    fis.close();

四、在SSH中使用smartUpload組件簡化文件上傳下載

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 任我行视频在线观看国语 | 二区三区视频 | 91免费在线播放 | 麻豆网站在线免费观看 | 欧美一区二区三区四区视频 | 国产精品自产拍在线观看2019 | 日本暖暖视频在线观看 | 四虎影院4hu | meyd—447佐山爱在线 | 国产精品永久免费视频 | 日日精品 | 欧美久久久久久久一区二区三区 | 99精品国产自在现线观看 | 成人伊人青草久久综合网破解版 | 国模娜娜a4u1546全套 | 国产成人啪精品午夜在线播放 | 暖暖 免费 高清 日本 中文 | 欧洲vodafonewi精品 | 女人和男人搞鸡 | 国产一区二区三区福利 | 高h短篇辣肉各种姿势bl | 555www成人网| 国产福利资源网在线观看 | 男人v天堂 | 精品久久久噜噜噜久久久app | 18无删减羞羞网站动漫 | 好男人资源免费观看 | 欧美成人午夜片一一在线观看 | 国产一卡2卡3卡四卡精品网 | 太深了 太粗h1v1 | 99精品热线在线观看免费视频 | 三级无删减高清在线影院 | 波多野结衣中文丝袜字幕 | 果冻传媒i91media免费 | 欧美日韩国产精品自在自线 | 激情婷婷综合久久久久 | 欧美日韩一二三区免费视频观看 | 免费观看大片毛片 | 校花被强迫np肉高h 校服下的白嫩小乳尖h1v1 | 亚洲国产成人久久综合一区77 | 拿捏小说 |