1. 程式人生 > >day20JavaWeb(檔案上傳)

day20JavaWeb(檔案上傳)

上傳不能使用BaseServlet 因為無法獲取引數了getParameter 方法不能用
檔案上傳概述
1、檔案上傳的作用
    例如網路硬碟,就是用來上傳下載檔案的
2、檔案上傳也對面的要求
    1、必須使用表單,而不能是超連結
    2、表單的method必須是POST,而不能是GET
    3、表單的enctype必須是multipart/form-data; 處理編碼
    4、在表單中新增file表單欄位,即<input type="file"name="xxx"/>
<form action="xxx" method ="post" enctype="multipart/form-data">
    <input type="text" name="username"/>    
    <input type="text" name="username"/>    
3、上次對Servlet限制
    request.getParameter("xxx");這個方法在表單為enctype="multipart/form-data"時,作廢了,永遠返回null
    ServletInputStream request.getInputStream
    ();包含整個請求的體
多部件表單的體
1、每隔出多個部件,即一個表單項一個部件
2、一個部件中自己包含請求頭和空行,以及請求體
3、普通表單項
    1個頭:Content-Disposition 包含name="xx",即表單項名稱
    體就是表單項的值
4、檔案表單項
    2個頭
        Content-Disposition:包含name="xxx"即表單項名稱:還有一個filename="xxx",表示上次檔案的名稱
        Content-Type:是上傳檔案的MIME型別 例如:image/pjpeg,表示上傳的是圖片,圖上中jpg副檔名的圖片
    體就是上傳檔案的內容
commons-fileupload
    commons-fileupload.jar 上傳依賴io
    commons-io.jar  可以獨立存       
可以幫我們解析request中的上傳資料,解析後的結果是一個表單項封裝到一個FileItem物件中,只需要呼叫FileItem的方法即可
1、上傳3步
    相關類:
        工廠:DiskFileItemFactory
        解析器:ServletFileUpload
        表單項:FileItem

    1、建立工廠:DiskFileItemFactory factory = new 工廠:DiskFileItemFactory();
    2、建立解析器:ServletFileUpload sfu = new 解析器:ServletFileUpload(factory);
    3、使用解析器來解析request:得到FileItem集合:List<FileItem> fileItemList = sfu.paraseRequest(request);

2、FileItem
    boolean isFormField():是否為普通表單項。返回true為普通表單項,如果為false即檔案表單項
    String getFieldName():返回當前表單項的名稱
    String getString(String charset):返回表單項的值
    String getName():返回上傳的檔名稱
    Long getSize():返回上傳檔案的位元組數
    InputStream getInputStream():返回上傳檔案對應的輸入流
    void write(File destFile):把上傳的檔案內容儲存到指定的檔案中
    String getContentType():獲取檔案型別

上傳的細節
1、檔案必須儲存到WEB-INF下
    不讓瀏覽器直接訪問到
    假如說使用者上傳了一個a.jsp檔案,然後使用者在通過瀏覽器去房屋呢這個a.jsp檔案,那麼就會執行a.jsp中的內容。
    如果在a.jsp中有Runtime.getRuntime().exec("shutdown -s -t 1");
                                                shutdown -a 取消

    通常會在WEB-INF目錄下建立一個uploads目錄來存放上傳的檔案,而在servlet中找到這個目錄需要使用ServletContext的getRealPath(Sting)方法,
        ServletContext servletContext = this.getServletContext();
        String savapath = servletContext.getRealPath("WEB-INF/uploads");
        其中savapath為:F:\tomcat8_5\webapps\uploads\WEB-INF\uploads
2、檔名稱相關問題
    有的瀏覽器(IE6)上傳的檔名是絕對路徑,需要切割 c:\files\aa.jpg
        無論是否為完整路徑,都去擷取最後一個"\\"後面的內容即可
        String filename = fi2.getName();
        int index = filename.lastIndexOf("\\");
        if(index!=-1){
            filename = filename.subString(index+1);
        }

    檔名亂碼或者普通表單項亂碼。request.setCharacterEncoding("utf-8");因為fileUpload內部會呼叫request.getCharacterEncoding("utf-8") 
        ServletFileUpload.setHeaderEncoding("utf-8");//優先順序高
    檔案同名問題:需要為每個檔案新增名稱字首:這個字首要保證不能重複。uuid
        filename = CommonUtils.uuid() + "_" + filename;
3、目錄打散
    不能在一個目錄下存放過多檔案 過萬檔案在一個資料夾會很卡
        首字元打散:使用檔案的首字母作為目錄名稱,例如abc.txt.那麼我們把檔案儲存到a目錄下,如果a目錄此時不存在,那麼建立
        時間打散:使用當前日期作為目錄
        雜湊打散:
            通過檔名稱得到int值,即呼叫hashCode()
            它int值轉換成16進位制0~9,A~F
            獲取16進位制的前兩位來生成目錄,目錄為二層。例如1B2C3D4E5F /1/B儲存檔案
4、上傳檔案的大小限制
    單個檔案大小限制    ServletFileUpload類的setFileSizeMax(long)即可,引數就是上傳檔案的上限位元組數,
例如servletFileUpload.setFileSizeMax(1024*10)表示上限為10kb 
    一旦超出會丟擲FileUploadBase.FileSizeLimitExceededException
    可以在sevlet獲取這個異常然後向頁面輸出
    必須在解析開始之前 parseRequest之前
    如果上傳的檔案超出限制,在parseRequest()方法執行時,會丟擲異常FileUploadBase.FileSizeLimitExceededException
    整個請求所有資料大小限制
    sfu.setSizeMax(1024*1024);//限制整個表單大小為1M
    這個方法也是必須在parseRequest()之前呼叫
    如果上傳的檔案超出限制,在parseRequest()方法執行時,會丟擲異常FileUploadBase.SizeLimitExceededException

5、快取大小與臨時目錄 
    快取大小:超出多大,才向硬碟儲存。預設10KB
    臨時目錄:向硬碟的什麼位置儲存
    設定快取大小與臨時目錄:new DiskFileItemFactory(20*1024,new Fil("F:/temp"))  

用到的jar包:
commons-io-2.6.jar
commons-fileupload-1.2.1.jar
itcast-tools-1.4.jar
commons-beanutils-1.8.3.jar
commons-logging-1.1.1.jar   
public class UploadServlet extends HttpServlet {

    public void
            doPost(HttpServletRequest request, HttpServletResponse response)
                    throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        /*
         * 上傳 1.得到工廠 2.通過工廠得到解析器 3.解析request得到fileTtem集合
         * 4.遍歷fileItem集合,呼叫其API儲存檔案
         * 
         * // 建立工廠 DiskFileItemFactory factory = new DiskFileItemFactory(); //
         * 建立解析器 ServletFileUpload fileUpload = new ServletFileUpload(factory);
         * // 使用解析器解析request try { List<FileItem> fileItems =
         * fileUpload.parseRequest(request); FileItem f1 = fileItems.get(0);
         * FileItem f2 = fileItems.get(1); System.out.println("普通表單項:" +
         * f1.getFieldName() + f1.getString("UTF-8"));
         * System.out.println("檔案表單項:"); System.out.println("FileNAme:" +
         * f2.getName()); System.out.println("ContetType:" +
         * f2.getContentType()); System.out.println("FileSize:" + f2.getSize());
         * // 儲存檔案 File destFile = new File("D:/圖片.jsp"); f2.write(destFile);
         * 
         * } catch (FileUploadException e) { throw new RuntimeException(e); }
         * catch (Exception e) { throw new RuntimeException(e); }
         */

        // 建立工廠
        DiskFileItemFactory factory = new DiskFileItemFactory(1 * 1024,
                new File("D:/資料"));
        // 建立解析器
        ServletFileUpload fileUpload = new ServletFileUpload(factory);
        /*
         * fileUpload.setFileSizeMax(5*1024); fileUpload.setSizeMax(1024*1024);
         */
        // 使用解析器解析request
        try {
            List<FileItem> fileItems = fileUpload.parseRequest(request);
            FileItem fi = fileItems.get(1);
            // ///////////////////////////////////////////////
            /*
             * 1.得到檔案儲存路徑
             */
            String root = this.getServletContext()
                    .getRealPath("/WEB-INF/files");
            /*
             * 2.生成二層目錄 1).得到檔名稱 2).得到hashcode 3).轉發成十六進位制 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 hashcode = fileName.hashCode();
            // 轉換成十六進位制
            String hex = Integer.toHexString(hashcode);
            /*
             * 2.得到前兩位字母,與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 FileSizeLimitExceededException) {
                request.setAttribute("msg", "上傳圖片過大,不能超過5kb");
                request.getRequestDispatcher("/index.jsp").forward(request,
                        response);
            }

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}
  <h3>${msg }</h3>
  <body>
    <form action="<c:url value='/UploadServlet'/>" method="post" enctype="multipart/form-data">
   		使用者名稱<input type="text" name="username"><br>
    	照 片<input type="file" name="zhaopian"><br>
    	<input type="submit" value="提交">
    </form>
  </body>