1. 程式人生 > >commons-fileupload 檔案上傳之細節

commons-fileupload 檔案上傳之細節

目錄
    1、把上傳的檔案放到WEB-INF目錄下
    2、檔名稱(完整路徑、檔名稱)
    3、中文亂碼問題
    4、上傳檔案同名問題(檔案重新命名)
    5、一個目錄不能存放過多的檔案(存放目錄打散)
    6、上傳的單個檔案的大小限制和整個表單大小限制
    7、快取大小與臨時目錄
======================================================
1、把上傳的檔案放到WEB-INF目錄下
    如果沒有把使用者上傳的檔案存放到WEB-INF目錄下,那麼使用者就可以通過瀏覽器直接訪問上傳的檔案,這是非常危險的。
    假如說使用者上傳了一個a.jsp檔案,然後使用者在通過瀏覽器去訪問這個a.jsp檔案,那麼就會執行a.jsp中的內容,
    如果在a.jsp中有如下語句:Runtime.getRuntime().exec("shutdown –s –t 1");,那麼你就會…
    通常我們會在WEB-INF目錄下建立一個uploads目錄來存放上傳的檔案,而在Servlet中找到這個目錄需要
    使用ServletContext的getRealPath(String)方法,例如在我的upload1專案中有如下語句:
        ServletContext servletContext = this.getServletContext();
        String savepath = servletContext.getRealPath("/WEB-INF/uploads");
        其中savepath為:"F:\tomcat6_1\webapps\upload1\WEB-INF\uploads"

2、檔名稱(完整路徑、檔名稱)
    上傳檔名稱可能是完整路徑:
    有些瀏覽器獲取的上傳檔名稱是完整路徑,大部分瀏覽器獲取的上傳檔名稱只是檔名稱而已。
    瀏覽器差異的問題我們還是需要處理一下的。處理這一問題也很簡單,無論是否為完整路徑,我們都去擷取最後一個"\\"後面的內容就可以了。
    String name = file1FileItem.getName();
    int lastIndex = name.lastIndexOf("\\");//獲取最後一個"\"的位置
    if(lastIndex != -1) {//注意,如果不是完整路徑,那麼就不會有"\"的存在。
        name = name.substring(lastIndex + 1);//獲取檔名稱
    }
    response.getWriter().print(name);

3、中文亂碼問題
    當上傳的誰的名稱中包含中文時,需要設定編碼,commons-fileupload元件為我們提供了兩種設定編碼的方式:
        * request.setCharacterEncoding(String):這種方式是我們最為熟悉的方式了;
        * fileUpload.setHeaderEncdoing(String):這種方式是commons-fileupload元件中的,優先順序高與前一種。
    上傳檔案的檔案內容包含中文:
    通常我們不需關心上傳檔案的內容,因為我們會把上傳檔案儲存到硬碟上!也就是說,檔案原來是什麼樣子,到伺服器這邊還是什麼樣子!
    但是如果你有這樣的需求,非要在控制檯顯示上傳的檔案內容,那麼你可以使用fileItem.getString("utf-8")來處理編碼。
    文字檔案內容和普通表單項內容使用FileItem類的getString("utf-8")來處理編碼。

4、上傳檔案同名問題(檔案重新命名)
    通常我們會把使用者上傳的檔案儲存到uploads目錄下,但如果使用者上傳了同名檔案呢?這會出現覆蓋的現象。
    處理這一問題的手段是使用UUID生成唯一名稱,然後再使用"_"連線檔案上傳的原始名稱。
    例如,使用者上傳的檔案是"我的一寸照片.jpg",在通過處理後,檔名稱為:"891b3881395f4175b969256a3f7b6e10_我的一寸照片.jpg",
    這種手段不會使檔案丟失副檔名,並且因為UUID的唯一性,上傳的檔案同名,但在伺服器端是不會出現同名問題的。

5、一個目錄不能存放過多的檔案(存放目錄打散)
        一個目錄下不應該存放過多的檔案,一般一個目錄存放1000個檔案就是上限了,如果在多,那麼開啟目錄時就會很"卡"。
    你可以嘗試列印C:\WINDOWS\system32目錄,你會感覺到的。也就是說,我們需要把上傳的檔案放到不同的目錄中。
    但是也不能為每個上傳的檔案一個目錄,這種方式會導致目錄過多。所以我們應該採用某種演算法來"打散"!打散的方法有很多,
    例如,使用日期來打散,每天生成一個目錄。也可以使用檔名的首字母來生成目錄,相同首字母的檔案放到同一目錄下。
    日期打散演算法:如果某一天上傳的檔案過多,那麼也會出現一個目錄檔案過多的情況;
    首字母打散演算法:如果檔名是中文的,因為中文過多,所以會導致目錄過多的現象。
    我們這裡使用hash演算法來打散:
        1.獲取檔名稱的hashCode:int hCode = name.hashCode();
        2.將hCode轉換成16進位制字元;
        3.使用這個16進位制的字元的前兩個字元生成目錄鏈。
    int hCode = name.hashCode();//獲取檔名的hashCode
    String dir = Integer.toHexString(hCode);
    //與檔案儲存目錄連線成完整路徑
    savepath = savepath + "/" + dir.charAt(0) + "/" + dir.charAt(1);
    //因為這個路徑可能不存在,所以建立成File物件,再建立目錄鏈,確保目錄在儲存檔案之前已經存在
    new File(savepath).mkdirs();

6、上傳的單個檔案的大小限制和整個表單大小限制
    限制上傳檔案的大小很簡單,ServletFileUpload類的setFileSizeMax(long)就可以了。引數就是上傳檔案的上限位元組數,
    例如servletFileUpload.setFileSizeMax(1024*10)表示上限為10KB。一旦上傳的檔案超出了上限,
    那麼就會丟擲FileUploadBase.FileSizeLimitExceededException異常。我們可以在Servlet中獲取這個異常,
    然後向頁面輸出"上傳的檔案超出限制"。
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.setCharacterEncoding("utf-8");
        DiskFileItemFactory dfif = new DiskFileItemFactory();
        ServletFileUpload fileUpload = new ServletFileUpload(dfif);
        fileUpload.setFileSizeMax(1024 * 10);  // 設定上傳的單個檔案的上限為10KB
        fileUpload.setSizeMax(1024 * 200);  //設定整個請求的上限為200KB
        try {
            List<FileItem> list = fileUpload.parseRequest(request);
            ......
            fileItem.write(file);
        } catch (Exception e) {
            // 判斷丟擲的異常的型別是否為FileUploadBase.FileSizeLimitExceededException
            // 如果是,說明上傳檔案時超出了限制。
            if(e instanceof FileUploadBase.FileSizeLimitExceededException) {
                request.setAttribute("msg", "上傳失敗!上傳的檔案超出了10KB!");
                request.getRequestDispatcher("/index.jsp").forward(request, response);
                return;
            }
            if(e instanceof FileUploadBase.SizeLimitExceededException) {
                request.setAttribute("msg", "上傳失敗!整個表單上傳的檔案超出了200KB!");
                request.getRequestDispatcher("/index.jsp").forward(request, response);
                return;
            }
            throw new ServletException(e);
        }
    }

7、快取大小與臨時目錄
    如果上傳一個藍光電影,先把電影儲存到記憶體中,然後再通過記憶體copy到伺服器硬碟上,那麼你的記憶體能吃的消麼?
    所以fileupload元件不可能把檔案都儲存在記憶體中,fileupload會判斷檔案大小是否超出10KB,
    如果超出,是那麼就把檔案儲存到硬碟上;如果沒有超出,那麼就儲存在記憶體中。
  10KB是fileupload預設的值,我們可以來設定它。
  當檔案儲存到硬碟時,fileupload是把檔案儲存到系統臨時目錄,當然你也可以去設定臨時目錄。
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws Exception {
        DiskFileItemFactory dfif = new DiskFileItemFactory(1024*20, new File("F:\\temp")); //設定快取大小20KB 及目錄
            ......
    }