檔案的上傳和下載—上傳的實現,注意事項
實現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));