1. 程式人生 > >JavaWeb筆記-22-檔案上傳、八大細節問題處理

JavaWeb筆記-22-檔案上傳、八大細節問題處理

1、檔案上傳


1)上傳檔案對錶單的限制
    1. method="post"
    2. enctype="multipart/form-data"   //多部件表單資料
    3. 表單中需要新增檔案表單選項:<input type="file" name="xxx" />

2)上傳檔案對Servlet的限制
    1.不能使用request.getParameter("xxx");來獲取表單引數  
        //因為此方法返回值為字串。

    2.獲取表單引數方法:ServletInputStream request.getInputStream();
        //包含整個請求的體!
    eg:
    <form action="" method="post" enctype="multipart/form-data">
          使用者名稱;<input type="text" name="username"/><br/>
          照 片:<input type="file" name="zhaoPian"/><br/>
          <input type="submit" value="上傳"/>
    </form> 

2、不同表單結構介紹:


1)多部件表單的體
        分割出多個部件。即一個表單項一個部件。
        每個部件包含:請求頭、空行、、請求體。

2)普通表單項:
    1個頭:
        Content-Disposition:包含name="xxxx",即表單項名稱。
    體:表單項的值

3)檔案表單項:
    2個頭:
        Content-Disposition:包含name="xxxx",即表單項名稱;
        filename="xxx",表示上傳檔案的名稱
         Content-Type:它是上傳檔案的MIME型別,例如:image/pjpeg,表示上傳的是圖片,圖上中jpg副檔名的圖片。
    體:上傳檔案的內容。


解析檔案的jar包:
    commons-fileupload.jar
    commons-io.jar   //依賴包

此包會幫我們解析request的上傳資料
解析結果:將每個表單項資料單獨封裝到FileItem物件中。使用時呼叫FileItem物件方法即可

3、上傳三步:


前提:匯入jar包:

步驟:
1)建立工廠:DiskFileItemFactory factory = new DiskFileItemFactory();

2)建立解析器:ServletFileUpload sfu = new ServletFileUpload(factory);

3)使用解析器解析request,得到FileItem集合:List<FileItem> fileItemList = sfu.parseRequest(request);

相關類:
    工廠: DiskFileItemFactory
    解析器:ServletUpload
    表單項:FileItem


相關方法:
FileItem
    1)boolean isFormField();是否為普通表單項。
            true:普通表單項
            false:檔案表單項
    2)String getFieldName(); 返回當前表單項的名稱。
    3)String getString(String charset);返回表單項的值。
    4)String getName(); 返回上傳的檔名稱
    5)long getSize();返回上傳檔案的位元組數
    6)InputStream getInputStream(); 返回檔案對應的輸入流。
    7)void write(File destFile); 把上傳的檔案儲存到指定的檔案中。

4、上傳的細節:


1.檔案必須儲存到WEB-INF下
     原因:不讓瀏覽器直接訪問到

2.檔名稱相關的三個問題。
    1)問題:少部分瀏覽器上傳的檔名是絕對路徑。
    解決1:所以需要切割檔名。只保留檔名,不保留路徑
    具體實施:判斷是否為絕對路徑程式碼
            String filename = xxx.getname();   //得到路徑名
            int index = filename.lastIndexOf("\\");  //檢視是否存在"\\"
            if(index != -1){                         //存在 
                filename = filename.subString(index+1);   //擷取掉"\\"部分路徑 
            }

    2)問題:檔名亂碼或表單項亂碼
    解決2: 1)request.setCharacterEncoding("utf-8"); //優先順序低
        2)servletFileUpload.setHeaderEncoding("utf-8");  //優先順序

    3)問題:檔案同名問題
    解決3:為檔案新增字首(藉助UUID)
    實施:filename = CommonUtils.uuid() + "_" + filename; 

3.目錄打散(分類、分層)
    不能在一個檔案下存放過多檔案。
        按首字元打散
        按時間打散
         ***hash打散:
              1)通過檔名獲得int值。即呼叫hashcode();
          2)將int值轉換為16進位制0~9、A~F
          3)獲取十六進位制的前兩位生成目錄。目錄為兩層。
            eg: 1B2C3D4E5F,  目錄為 /1/B 兩層

    知識點:hashCode是jdk根據物件的 地址/字串/數字 算出來的int型別的數值
        public int hashCode()返回該物件的雜湊碼值。


4.上傳檔案大小限制
    對單個檔案大小限制
        // sfu.setFileSizeMax(100 * 1024);//限制單個檔案大小為100K

    對整個請求所有資料大小限制
        // sfu.setSizeMax(1024 * 1024);//限制整個表單大小為1M

    注:限制語句必須在檔案解析之前

5.快取大小與臨時目錄
    快取:當上傳檔案超出快取大小設定值後,將不會存入記憶體,而是存入硬碟中的臨時目錄
    臨時目錄:當檔案存入記憶體後會被刪除。

    伺服器儲存檔案細節:
    最終目標:伺服器會儲存檔案到記憶體。

    特殊情況:當檔案過大時,檔案會先儲存到硬碟中,然後再寫入記憶體(指定目錄)
    JVM預設記憶體為64M

5、上傳程式碼演示


/**
 * 1.上傳檔案
 * 2.處理細節問題
 * @author 一萬年行不行
 *
 */

public class Upload3Servlet extends HttpServlet {
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");

        /*
         * 上傳三步
         */
        // 工廠
        DiskFileItemFactory factory = new DiskFileItemFactory();

//      設定快取檔案              
//      DiskFileItemFactory factory = new DiskFileItemFactory(20*1024, new File("F:/temp"));

        // 解析器
        ServletFileUpload sfu = new ServletFileUpload(factory);
//      sfu.setFileSizeMax(10 * 1024);//限制單個檔案大小為10K
//      sfu.setSizeMax(1024 * 1024);//限制整個表單大小為1M

        // 解析,得到List
        try {
            List<FileItem> list = sfu.parseRequest(request);
            FileItem fi = list.get(1);


            //生成檔案儲存目錄:
            //////////////////////////////////////////////////////


             //1. 得到儲存檔案的根路徑 (由自己設定)
            String root = this.getServletContext().getRealPath("/WEB-INF/files/");
            System.out.println(root);
            /*
             * 2. 生成二層目錄
             *   1). 得到檔名稱
             *   2). 得到hashCode
             *   3). 轉發成16進位制
             *   4). 獲取前二個字元用來生成目錄
             */
            String filename = fi.getName();//獲取上傳的檔名稱

            //處理檔名的絕對路徑問題
            int index = filename.lastIndexOf("\\");
            if(index != -1) {
                filename = filename.substring(index+1);
            }

            //給檔名稱新增uuid字首,處理檔案同名問題
            String savename = CommonUtils.uuid() + "_" + filename;


            //1. 得到hashCode
            int hCode = filename.hashCode();
            String hex = Integer.toHexString(hCode);


            //2. 獲取hex的前兩個字母,與root連線在一起,生成一個完整的路徑
            File dirFile = new File(root, hex.charAt(0) + "/" + hex.charAt(1));


            //3. 根據生成路徑  建立目錄鏈
            dirFile.mkdirs();

            //4. 建立目標檔案 (將檔案地址和檔名繫結)
            File destFile = new File(dirFile, savename);

             //5. 儲存  (將檔案內容寫入 目標檔案)
            fi.write(destFile);

            ///////////////////////////////////////////////////////

        } catch (FileUploadException e) {
            if(e instanceof FileUploadBase.FileSizeLimitExceededException) {
                request.setAttribute("msg", "您上傳的檔案超出了10KB!");
                request.getRequestDispatcher("/index.jsp").forward(request, response);
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}