1. 程式人生 > >檔案的上傳和下載—上傳的實現,注意事項

檔案的上傳和下載—上傳的實現,注意事項

 實現WEB開發中的檔案上傳功能,需完成如下二步操作:

在WEB頁面中新增上傳輸入項,<input type=“life” name=“”>,使用時注意:

1. 必須要設定input輸入項的name屬性,否則瀏覽器將不會發送上傳檔案的資料。

2. 必須把input項的enctype屬值設為multipart/form-data.設定該值後,瀏覽器在上傳檔案時,將把檔案資料附帶在http請求訊息體中,並使用MIME協議對上傳的檔案進行描述,以方便接收方對上傳資料進行解析和處理。

如何在Servlet中讀取檔案上傳資料,並儲存到伺服器本地硬碟中?

Request物件提供了一個getInputStream()方法,使用這個方法可以獲取瀏覽器提交過來的輸入流。但如果瀏覽器上傳多個檔案時,我們應該如何區分開來?這是一項複雜的工作。

為了方便使用者處理檔案上傳資料,Apache開源組織提供了用於處理上面應用的開源元件——Commons-fileupload。這個元件使用簡單,效能也比較優異,所以幾乎都使用它來實現上傳檔案的處理功能。

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")。

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),設定編碼格式。如果檔案路徑中存在中文可能會造成檔案路徑亂碼,用此方法處理可以解決。

上傳檔案案例:

public class FileuploadServlet extends HttpServlet { 



public void dopost(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 = new byte[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 doGet(HttpServletRequest request, HttpServletResponse response) 

throws ServletException, IOException { 

doPost(request, response); 

} 

} 

上傳檔案的處理細節(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檔名稱

* @param fileName

* @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));