JavaEE系列之(二)commons-fileupload實現文件上傳、下載

1、文件上傳概述html

    實現Web開發中的文件上傳功能,須要兩步操做:
    一、在Web頁面中添加上傳輸入項
  1.        <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>
           <!-- 一、表單方式必須是post
                二、必須設置encType屬性爲 multipart/form-data.設置該值後,瀏覽器在上傳文件時,將會把文件數據附帶在http請求消息體中,
    並使用MIME協議對上傳的文件進行描述,以方便接收方對上傳數據進行解析和處理。 三、必需要設置input的name屬性,不然瀏覽器將不會發送上傳文件的數據。
    -->

         二、在Servlet中讀取文件上傳數據,並保存到服務器硬盤java

             Request對象提供了一個getInputStream方法,經過這個方法能夠讀取到客戶端提交過來的數據。但因爲用戶可能會同時上傳多個文件,在Servlet端編程直接讀取上傳數據,並分別解析出相應的文件數據是一項很是麻煩的工做。
             好比下面是截取的瀏覽器上傳文件時發送的請求的HTTP協議中的部份內容:         
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--

             從上面的數據中也能夠看出,若是本身手工的去分割讀取數據很難寫出健壯穩定的程序。因此,爲方便用戶處理上傳數據,Apache開源組織提供了一個用來處理表單文件上傳的一個開源組件(Commons-fileupload),該組件性能優異,而且其API使用極其簡單,可讓開發人員輕鬆實現web文件上傳功能,所以在web開發中實現文件上傳功能,一般使用Commons-fileupload組件實現。web

             須要導入兩個jar包:Commons-fileupload、commons-io
  1         response.setContentType("text/html;charset=utf-8");//設置響應編碼
  2         request.setCharacterEncoding("utf-8");
  3         PrintWriter writer = response.getWriter();//獲取響應輸出流
  4         
  5         ServletInputStream inputStream = request.getInputStream();//獲取請求輸入流
  6         
  7         /*
  8          * 一、建立DiskFileItemFactory對象,設置緩衝區大小和臨時文件目錄
  9          *    該類有兩個構造方法一個是無參的構造方法,
 10          *    另外一個是帶兩個參數的構造方法
 11          * @param  int sizeThreshold,該參數設置內存緩衝區的大小,默認值爲10K。當上傳文件大於緩衝區大小時,fileupload組件將使用臨時文件緩存上傳文件
 12          * @param  java.io.File repository,該參數指定臨時文件目錄,默認值爲System.getProperty("java.io.tmpdir");
 13          * 
 14          *    若是使用了無參的構造方法,則使用setSizeThreshold(int sizeThreshold),setRepository(java.io.File repository)
 15          *    方法手動進行設置 
 16          */
 17         DiskFileItemFactory factory = new DiskFileItemFactory();
 18         
 19         int sizeThreshold=1024*1024;
 20         factory.setSizeThreshold(sizeThreshold);
 21         
 22         File repository = new File(request.getSession().getServletContext().getRealPath("temp"));
 23 //        System.out.println(request.getSession().getServletContext().getRealPath("temp"));
 24 //        System.out.println(request.getRealPath("temp"));
 25         factory.setRepository(repository);
 26         
 27         /*
 28          * 二、使用DiskFileItemFactory對象建立ServletFileUpload對象,並設置上傳文件的大小
 29          *    
 30          *    ServletFileUpload對象負責處理上傳的文件數據,並將表單中每一個輸入項封裝成一個FileItem
 31          *    該對象的經常使用方法有:
 32          *           boolean isMultipartContent(request);判斷上傳表單是否爲multipart/form-data類型
 33          *           List parseRequest(request);解析request對象,並把表單中的每個輸入項包裝成一個fileItem 對象,並返回一個保存了全部FileItem的list集合
 34          *           void setFileSizeMax(long filesizeMax);設置單個上傳文件的最大值
 35          *           void setSizeMax(long sizeMax);設置上傳溫江總量的最大值
 36          *           void setHeaderEncoding();設置編碼格式,解決上傳文件名亂碼問題
 37          */
 38         ServletFileUpload upload = new ServletFileUpload(factory);
 39         
 40         upload.setHeaderEncoding("utf-8");//設置編碼格式,解決上傳文件名亂碼問題
 41         /*
 42          * 三、調用ServletFileUpload.parseRequest方法解析request對象,獲得一個保存了全部上傳內容的List對象
 43          */
 44         List<FileItem> parseRequest=null;
 45         try {
 46              parseRequest = upload.parseRequest(request);
 47         } catch (FileUploadException e) {
 48             e.printStackTrace();
 49         }
 50         /*
 51          * 四、對list進行迭代,每迭代一個FileItem對象,調用其isFormField方法判斷是不是文件上傳
 52          *    true表示是普通表單字段,則調用getFieldName、getString方法獲得字段名和字段值
 53          *    false爲上傳文件,則調用getInputStream方法獲得數據輸入流,從而讀取上傳數據
 54          *    
 55          *    FileItem用來表示文件上傳表單中的一個上傳文件對象或者普通的表單對象
 56          *    該對象經常使用方法有:
 57          *         boolean isFormField();判斷FileItem是一個文件上傳對象仍是普通表單對象
 58          *         true表示是普通表單字段,
 59          *                 則調用getFieldName、getString方法獲得字段名和字段值
 60          *         false爲上傳文件,
 61          *                 則調用getName()得到上傳文件的文件名,注意:有些瀏覽器會攜帶客戶端路徑,須要本身減除
 62          *                 調用getInputStream()方法獲得數據輸入流,從而讀取上傳數據
 63          *                 delete(); 表示在關閉FileItem輸入流後,刪除臨時文件。
 64          */
 65         
 66         for (FileItem fileItem : parseRequest) {
 67             if (fileItem.isFormField()) {//表示普通字段
 68                 if ("username".equals(fileItem.getFieldName())) {
 69                     String username = fileItem.getString();
 70                     writer.write("您的用戶名:"+username+"<br>");
 71                 }
 72                 if ("userpass".equals(fileItem.getFieldName())) {
 73                     String userpass = fileItem.getString();
 74                     writer.write("您的密碼:"+userpass+"<br>");
 75                 }
 76                 
 77             }else {//表示是上傳的文件
 78                 //不一樣瀏覽器上傳的文件可能帶有路徑名,須要本身切割
 79                 String clientName = fileItem.getName();
 80                 String filename = "";
 81                 if (clientName.contains("\\")) {//若是包含"\"表示是一個帶路徑的名字,則截取最後的文件名
 82                     filename = clientName.substring(clientName.lastIndexOf("\\")).substring(1);
 83                 }else {
 84                     filename = clientName;
 85                 }
 86                 
 87                 UUID randomUUID = UUID.randomUUID();//生成一個128位長的全球惟一標識
 88                 
 89                 filename = randomUUID.toString()+filename;
 90                 
 91                 /*
 92                  * 設計一個目錄生成算法,若是所用用戶上傳的文件總數是億數量級的或更多,放在同一個目錄下回致使文件索引很是慢,
 93                  * 因此,設計一個目錄結構來分散存放文件是很是有必要,且合理的
 94                  * 將UUID取哈希算法,散列到更小的範圍,
 95                  * 將UUID的hashcode轉換爲一個8位的8進制字符串,
 96                  * 從這個字符串的第一位開始,每個字符表明一級目錄,這樣就構建了一個八級目錄,每一級目錄中最多有16個子目錄
 97                  * 這不管對於服務器仍是操做系統都是很是高效的目錄結構
 98                  */
 99                 int hashUUID =randomUUID.hashCode();
100                 String hexUUID = Integer.toHexString(hashUUID);
101                 //System.out.println(hexUUID);
102                 //獲取將上傳的文件存存儲在哪一個文件夾下的絕對路徑
103                 String filepath=request.getSession().getServletContext().getRealPath("upload");
104                 for (char c : hexUUID.toCharArray()) {
105                     filepath = filepath+"/"+c;
106                 }
107                 //若是目錄不存在就生成八級目錄
108                 File filepathFile = new File(filepath);
109                 if (!filepathFile.exists()) {
110                     filepathFile.mkdirs();
111                 }
112                 //從Request輸入流中讀取文件,並寫入到服務器
113                 InputStream inputStream2 = fileItem.getInputStream();
114                 //在服務器端建立文件
115                 File file  = new File(filepath+"/"+filename);
116                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
117                 
118                 byte[] buffer = new byte[10*1024];
119                 int len = 0;
120                 while ((len= inputStream2.read(buffer, 0, 10*1024))!=-1) {
121                     bos.write(buffer, 0, len);
122                 }
123                 writer.write("您上傳文件"+clientName+"成功<br>");
124                 //關閉資源
125                 bos.close();
126                 inputStream2.close();
127             }
128         }
129     //注意Eclipse的上傳的文件是保存在項目的運行目錄,而不是workspace中的工程目錄裏。

 

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

       一、文件存放的位置
          爲保證服務器的安全,上傳文件應保存在應用程序的WEB-INF目錄下,或者不受WEB服務器管理的目錄,若是用戶上傳一個帶有可執行代碼的文件,如jsp文件,根據拼接訪問路徑去訪問的話,能夠在服務器端作任何事情。
       二、爲防止多用戶上傳形同文件名的文件,而致使文件覆蓋的狀況發生,文件上傳程序應保證上傳文件具備惟一文件名。
          使用UUID + 用戶上傳文件名的方式重命名
          關於UUID:
             UUID(Universally Unique Identifier)全局惟一標識符,是指在一臺機器上生成的數字,它保證對在同一時空中的全部機器都是惟一的。按照開放軟件基金會(OSF)制定的標準計算,用到了以太網卡地址、納秒級時間、芯片ID碼和許多可能的數字。由如下幾部分的組合:當前日期和時間(UUID的第一個部分與時間有關,若是你在生成一個UUID以後,過幾秒又生成一個UUID,則第一個部分不一樣,其他相同),時鐘序列,全局惟一的IEEE機器識別號(若是有網卡,從網卡得到,沒有網卡以其餘方式得到),UUID的惟一缺陷在於生成的結果串會比較長。
            是一個128位長的數字,通常用16進製表示。算法的核心思想是結合機器的網卡、當地時間、一個隨即數來生成GUID。從理論上講,若是一臺機器每秒產生10000000個GUID,則能夠保證(機率意義上)3240年不重複。
            從JDK1.5開始,生成UUID變成了一件簡單的事,覺得JDK實現了UUID:
java.util.UUID,直接調用便可.
UUID uuid  =  UUID.randomUUID();
String s = UUID.randomUUID().toString();//用來生成數據庫的主鍵id很是不錯。。  
 
UUID是由一個十六位的數字組成,表現出來的形式例如
550E8400-E29B-11D4-A716-446655440000 

       三、爲防止單個目錄下文件過多,影響文件讀寫速度,處理上傳文件的程序應該應根據可能的上傳總量,選擇合適的目錄結構生成算法,將上傳文件分散存儲。如使用hashcode方法構建多級目錄。數據庫

       四、若是不一樣用戶都上傳了相同的文件,那麼在服務器端沒有必要存儲同一個文件的不少分拷貝,這樣很浪費資源,應該設計算法解決這種重複文件的問題。
       五、JSP技術原理自動實現了多線程。因此開發者不須要考慮上傳文件的多線程操做
 
3、文件下載  
<%
        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();

 

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



相關文章
相關標籤/搜索