隨筆-48  評論-26  文章-0  trackbacks-0

          核心API-DiskFileItemFactory:

               DiskFileItemFactory是創建FileItem對象的工廠,這個工廠常用方法:

          1.      public DiskFileItemFactory(int sizeThreshold, java.io.File repository),常用的構造函數。

          2.      public void setSizeThreshold(int sizeThreshold),設置內存緩沖區的大小,默認值為10K。當上傳文件大于緩沖區大小時, fileupload組件將使用臨時文件緩存上傳文件。

          3.      public void setRepository(java.io.File repository),指定臨時文件目錄,默認值為System.getProperty("java.io.tmpdir")。

                  

          核心API-ServletFileupLoad:

          ServletFileUpload 負責處理上傳的文件數據,并將表單中每個輸入項封裝到一個FileItem對象中。常用方法有:

          1.      boolean isMultipartContent(HttpServletRequest request),判斷上傳表單是否為上傳表單類型。

          2.      List parseRequest(HttpServletRequest request),解析request對象,并把表單中的每一個輸入項包裝到一個fileItem 對象中,并返回一個保存了所有FileItem的list集合。

          3.      setFileSizeMax(long fileSizeMax),設置上傳文件的最大尺寸值。

          4.      setSizeMax(long sizeMax),設置上傳文件總量的最大值。

          5.      setHeaderEncoding(java.lang.String encoding),設置編碼格式。如果文件路徑中存在中文可能會造成文件路徑亂碼,用此方法處理可以解決。

          6.      setProgressListener(ProgressListener pListener),設置進程監聽器,與AWT和Swing的事件處理機制一樣。文件上傳一點就會觸發ProgressListener,這樣我們就可以獲取文件上傳的進度。

          上傳文件案例:

          public class FileuploadServlet extends HttpServlet {

              public void doGet(HttpServletRequest request, HttpServletResponse response)

                     throws ServletException, IOException {

                 // 創建文件處理工廠,它用于生成FileItem對象。

                 DiskFileItemFactory difactory = new DiskFileItemFactory();

                 //設置緩存大小,如果上傳文件超過緩存大小,將使用臨時目錄做為緩存。

                 difactory.setSizeThreshold(1024 * 1024);

                 // 設置處理工廠緩存的臨時目錄,此目錄下的文件需要手動刪除。

                 String dir = this.getServletContext().getRealPath("/");

                 File filedir = new File(dir + "filetemp");

                 if (!filedir.exists())

                     filedir.mkdir();

                 difactory.setRepository(filedir);

                 // 設置文件實際保存的目錄

                 String userdir = dir + "files";

                 File fudir = new File(userdir);

                 if (!fudir.exists())

                     fudir.mkdir();

                 // 創建request的解析器,它會將數據封裝到FileItem對象中。

                 ServletFileUpload sfu = new ServletFileUpload(difactory);

                 // 解析保存在request中的數據并返回list集合

                 List list = null;

                 try {

                     list = sfu.parseRequest(request);

                 } catch (FileUploadException e) {

                     e.printStackTrace();

                 }

                 // 遍歷list集合,取出每一個輸入項的FileItem對象,并分別獲取數據

                 for (Iterator it = list.iterator(); it.hasNext();) {

                     FileItem fi = (FileItem) it.next();

                     if (fi.isFormField()) {

                        System.out.println(fi.getFieldName());

                        System.out.println(fi.getString());

                     } else {

                        //由于客戶端向服務器發送的文件是客戶端的全路徑,在這我們只需要文件名即可

                        String filename = fi.getName();

                        int index = filename.lastIndexOf("""");

                        if(index != -1)

                            filename = filename.substring(index+1);

                        //向服務器寫出文件

                        InputStream in = fi.getInputStream(); 

                        FileOutputStream fos = new FileOutputStream(fudir + "/" +filename);

                        byte[] buf = newbyte[1024];

                        int len = -1;

                        while((len = in.read(buf)) != -1){

                            fos.write(buf, 0, len);

                        }

                        // 關閉流

                        if(in != null){

                            try{

                               in.close();

                            }finally{

                               if(fos!=null)

                                   fos.close();

                            }

                        }

                     }

                 }

              }

              public void doPost(HttpServletRequest request, HttpServletResponse response)

                     throws ServletException, IOException {

                 doGet(request, response);

              }

          }

          上面的代碼只是功能的練習,實際開發中的文件上傳需要考慮諸多因素,我們接下來繼續學習。

              

          JS動態添加文件上傳框和按鈕的JavaScript代碼:

          function add(){

                    var file = document.createElement("input");

                    file.type = "file";

                    file.name = "file";

                   

                    var butt = document.createElement("input");

                    butt.type = "button";

                    butt.value = "刪除";

                    butt.onclick = function rem(){

                    //必須使用按鈕的父節點DIV的父節點來刪除自己和自己的父節點DIV

                        this.parentNode.parentNode.removeChild(this.parentNode);

                    };

                   

                    var div = document.createElement("div");

                    div.appendChild(file);

                    div.appendChild(butt);

                   

                    var parent = document.getElementById("files");

                    parent.appendChild(div);

                 }

          上傳文件的處理細節(1):

          1.      中文文件亂碼的問題,可以調用兩個方法來設置字符編碼:servletUpLoader.setHeaderEncoding()或request.setCharacterEncoding()。我們可以在源文件的創建ServletFileUpload對象后邊添加如下代碼:

          sfu.setHeaderEncoding("UTF-8");

          2.      臨時文件的刪除,如果臨時文件大于setSizeThreshold設置的緩存大小,Commons-fileupload組件將使用setRepository設置的臨時目錄來保存上傳的文件,上傳完成后我們需要手動調用FileItem.delete來刪除臨時文件。建議不要修改緩存區大小,如果設置緩存為1MB,1000個用戶上傳文件就需要1000MB內存,服務器會受不了的。我們刪除掉setSizeThreshold的代碼,并在每次完成一個文件后添加下而的代碼:

          // 刪除臨時目錄中的文件

          fi.delete();

          上傳文件的處理細節(2):

          1.      在上面的代碼中,我們將文件的實際保存目錄設置在WEB-INF目錄之外。這樣外部可以直接訪問被上傳的文件,這會造成安全問題。比如用戶上傳了一個帶有惡意腳本功能的JSP文件,然后從外部訪問執行了JSP文件…后果不堪設想。所以我們將源代碼中對應位置處修改如下:

          // 之所以放在"WEB-INF"目錄下是為了防止上傳的文件被直接被訪問的安全問題

          String userdir = dir + "WEB-INF/files";

          2.      一個WEB應用會許多不同的用戶訪問,不同的用戶可能會上傳相同名稱的文件,如果這樣可能會造成文件覆蓋的情況發生,所以我們必須保證文件名稱的唯一性,我編寫一個方法來生成唯一性名稱的文件名:

          /**

               *生成具有唯一性的UUID文件名稱

               *@paramfileName

               *@return

               */

              private String uuidName(String fileName){

                 UUID uuid = UUID.randomUUID();

                 return uuid.toString() + "_" + fileName;

              }

          我們將代碼“filename = filename.substring(index + 1);”修改為:filename = uuidName(filename.substring(index + 1));

          3.      如果一個目錄下的文件過多,會極大減慢文件的訪問速度。比如一個目錄下的文件如果超過1000個,達到1萬個呢?恐怖!我們必須編寫一個目錄結構生成算法,來分散存上傳的文件。我們一個方法:

              /**

               *使用哈希算法生成的文件路徑

               *@paramdir

               *@paramfileName

               *@return

               */

              private String hashPath(String dir, String fileName) {

                 int hashCode = fileName.hashCode();

                

                 int dir1 = (hashCode >> 4) & 0xf;

                 int dir2 = hashCode & 0xf;

                

                 String newpath = dir + "/" + dir1 + "/" + dir2 + "/";

                 File file = new File(newpath);

                 if(!file.exists()){

                     file.mkdirs();

                 }

                

                 return newpath + uuidName(fileName);

              }

          上傳文件的處理細節(3)

          1.      使用ProgressListener顯示上傳文件進度,在創建ServletFileUpload之后添加如下代碼:

          // 設置文件上傳進度監聽器

                 sfu.setProgressListener(new ProgressListener() {

                     public void update(long pBytesRead, long pContentLength, int pItems) {

                        System.out.println("已上傳:" + pBytesRead + "總大小:"

                               + pContentLength);

                     }

          });

          2.      上面的代碼會造成頻繁的打印,為了使它在上傳一定數量后再打印,比如上傳10KB后再打印,我們修改上面的代碼如下:

          // 設置文件上傳進度監聽器

                 sfu.setProgressListener(new ProgressListener() {

                     longtemp = -1;

                     public void update(long pBytesRead, long pContentLength, int pItems) {

                        long size = pBytesRead / 1024 * 1024 * 10;

                        if(temp == size)

                            return;

                        temp = size;

                        if(pBytesRead != -1)

                            System.out.println("已上傳:" + pBytesRead + "總大小:"

                               + pContentLength);

                        else

                            System.out.println("上傳完成!");

                     }

          });

          上面的代碼比較經典,好好回味一下。

          文件下載:

              WEB應用中實現文件下載的兩種方式:

          1.         超鏈接直接指向下載資源

          2.         程序實現下載需設置兩個響應頭:

          (1). 設置Content-Type 的值為:application/x-msdownload。Web 服務器需要告訴瀏覽器其所輸出的內容的類型不是普通的文本文件或 HTML 文件,而是一個要保存到本地的下載文件。

          (2). Web 服務器希望瀏覽器不直接處理相應的實體內容,而是由用戶選擇將相應的實體內容保存到一個文件中,這需要設置 Content-Disposition 報頭。該報頭指定了接收程序處理數據內容的方式,在 HTTP 應用中只有 attachment 是標準方式,attachment 表示要求用戶干預。在 attachment 后面還可以指定 filename 參數,該參數是服務器建議瀏覽器將實體內容保存到文件中的文件名稱。在設置 Content-Dispostion 之前一定要指定 Content-Type。

               為實現文件下載,首先我們遍歷目錄下所有文件,Servlet:

          import java.io.File;

          import java.io.IOException;

          import java.util.HashMap;

          import javax.servlet.ServletException;

          import javax.servlet.http.HttpServlet;

          import javax.servlet.http.HttpServletRequest;

          import javax.servlet.http.HttpServletResponse;

          public class ListFileServlet extends HttpServlet {

              public void doGet(HttpServletRequest request, HttpServletResponse response)

                     throws ServletException, IOException {

                 // 獲取目錄

                 String dir = this.getServletContext().getRealPath("/WEB-INF/files");

                 HashMap map = new HashMap();

                 listFile(new File(dir), map);

                 // 將文件列表設置到request的屬性中,然后由JSP頁面打印列表。

                 request.setAttribute("filemap", map);

                 request.getRequestDispatcher("/list.jsp").forward(request, response);

              }

              /**

               *使用遞歸算法,將所有子目錄中的文件添加到列表中

               *

               *@paramf

               *@paraml

               */

              private void listFile(File f, HashMap map) {

                 if (f.isFile()) {

                     String path = f.getAbsolutePath().substring(

                            this.getServletContext().getRealPath("/").length());

                     String name = f.getName();

                     name = name.substring(name.indexOf("_")+1);         

                     //BASE64Encoder encoder = new BASE64Encoder();

                     map.put(path, name);

                 } else {

                     File[] files = f.listFiles();

                     for (int i = 0; i < files.length; i++) {

                        listFile(files[i], map);

                     }

                 }

              }

              public void doPost(HttpServletRequest request, HttpServletResponse response)

                     throws ServletException, IOException {

                 doGet(request, response);

              }

          }
          posted on 2009-12-02 14:14 Worker 閱讀(478) 評論(0)  編輯  收藏 所屬分類: J2SE/J2EE

          主站蜘蛛池模板: 万山特区| 玉林市| 融水| 乡宁县| 新竹县| 满洲里市| 茂名市| 海盐县| 贺兰县| 沙湾县| 郴州市| 万载县| 香河县| 雷山县| 铜山县| 临猗县| 叙永县| 太和县| 香河县| 广宁县| 浑源县| 偃师市| 通渭县| 顺义区| 开封市| 东莞市| 专栏| 左云县| 邢台县| 突泉县| 将乐县| 漳浦县| 婺源县| 五河县| 溆浦县| 嵊泗县| 长兴县| 镇安县| 祁东县| 蒙阴县| 台湾省|