l 文件上傳概述
l 實現web開發中的文件上傳功能,需完成如下二步操作:
l 在web頁面中添加上傳輸入項
l 在servlet中讀取上傳文件的數據,並保存到本地硬盤中。
l 如何在web頁面中添加上傳輸入項?
l <input type=“file”>標籤用於在web頁面中添加文件上傳輸入項,設置文件上傳輸入項時須注意:
l 1、必須要設置input輸入項的name屬性,否則瀏覽器將不會發送上傳文件的數據。
l 2、必須把form的enctype屬值設爲multipart/form-data.設置該值後,瀏覽器在上傳文件時,將把文件數據附帶在http請求消息體中,並使用MIME協議對上傳的文件進行描述,以方便接收方對上傳數據進行解析和處理。
l 如何在Servlet中讀取文件上傳數據,並保存到本地硬盤中?
l Request對象提供了一個getInputStream方法,通過這個方法可以讀取到客戶端提交過來的數據。但由於用戶可能會同時上傳多個文件,在servlet端編程直接讀取上傳數據,並分別解析出相應的文件數據是一項非常麻煩的工作,示例。
l 爲方便用戶處理文件上傳數據,Apache 開源組織提供了一個用來處理表單文件上傳的一個開源組件( Commons-fileupload ),該組件性能優異,並且其API使用極其簡單,可以讓開發人員輕鬆實現web文件上傳功能,因此在web開發中實現文件上傳功能,通常使用Commons-fileupload組件實現。
l 使用Commons-fileupload組件實現文件上傳,需要導入該組件相應的支撐jar包:Commons-fileupload和commons-io。commons-io 不屬於文件上傳組件的開發jar文件,但Commons-fileupload 組件從1.1 版本開始,它工作時需要commons-io包的支持。
l 核心API—DiskFileItemFactory
l DiskFileItemFactory 是創建 FileItem 對象的工廠,這個工廠類常用方法:
l public void setSizeThreshold(int sizeThreshold) :設置內存緩衝區的大小,默認值爲10K。當上傳文件大於緩衝區大小時, fileupload組件將使用臨時文件緩存上傳文件。
l public void setRepository(java.io.File repository) :指定臨時文件目錄,默認值爲System.getProperty("java.io.tmpdir").
l public DiskFileItemFactory(int sizeThreshold, java.io.File repository) :構造函數
l 核心API—ServletFileUpload
l ServletFileUpload 負責處理上傳的文件數據,並將表單中每個輸入項封裝成一個 FileItem 對象中。常用方法有:
• boolean isMultipartContent(HttpServletRequest request) :判斷上傳表單是否爲multipart/form-data類型
• List parseRequest(HttpServletRequest request):解析request對象,並把表單中的每一個輸入項包裝成一個fileItem 對象,並返回一個保存了所有FileItem的list集合。
• setFileSizeMax(long fileSizeMax) :設置上傳文件的最大值
• setSizeMax(long sizeMax) :設置上傳文件總量的最大值
• setHeaderEncoding(java.lang.String encoding) :設置編碼格式
• setProgressListener(ProgressListener pListener)
l 上傳文件的處理細節(1)
l 中文文件亂碼問題
• 文件名中文亂碼問題,可調用ServletUpLoader的setHeaderEncoding方法,或者設置request的setCharacterEncoding屬性
l 臨時文件的刪除問題
• 由於文件大小超出DiskFileItemFactory.setSizeThreshold方法設置的內存緩衝區的大小時,Commons-fileupload組件將使用臨時文件保存上傳數據,因此在程序結束時,務必調用FileItem.delete方法刪除臨時文件。
• Delete方法的調用必須位於流關閉之後,否則會出現文件佔用,而導致刪除失敗的情況。
l 上傳文件的處理細節(2)
l 文件存放位置
• 爲保證服務器安全,上傳文件應保存在應用程序的WEB-INF目錄下,或者不受WEB服務器管理的目錄。
• 爲防止多用戶上傳相同文件名的文件,而導致文件覆蓋的情況發生,文件上傳程序應保證上傳文件具有唯一文件名。
• 爲防止單個目錄下文件過多,影響文件讀寫速度,處理上傳文件的程序應根據可能的文件上傳總量,選擇合適的目錄結構生成算法,將上傳文件分散存儲。
l 上傳文件的處理細節(3)
l ProgressListener顯示上傳進度
ProgressListener progressListener = new ProgressListener() {
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("到現在爲止, " + pBytesRead + " 字節已上傳,總大小爲 "
+ pContentLength);
}
};
upload.setProgressListener(progressListener);
l 以KB爲單位顯示上傳進度
long temp = -1; //temp注意設置爲類變量
long ctemp = pBytesRead /1024;
if (mBytes == ctemp)
return;
temp = mBytes;
l Web應用中實現文件下載的兩種方式
l 超鏈接直接指向下載資源
l 程序實現下載需設置兩個響應頭:
l 設置Content-Type 的值爲:application/x-msdownload。Web 服務器需要告訴瀏覽器其所輸出的內容的類型不是普通的文本文件或 HTML 文件,而是一個要保存到本地的下載文件。
l Web 服務器希望瀏覽器不直接處理相應的實體內容,而是由用戶選擇將相應的實體內容保存到一個文件中,這需要設置 Content-Disposition 報頭。該報頭指定了接收程序處理數據內容的方式,在 HTTP 應用中只有 attachment 是標準方式,attachment 表示要求用戶干預。在 attachment 後面還可以指定 filename 參數,該參數是服務器建議瀏覽器將實體內容保存到文件中的文件名稱。在設置 Content-Dispostion 之前一定要指定 Content-Type.
l 因爲要下載的文件可以是各種類型的文件,所以要將文件傳送給客戶端,其相應內容應該被當做二進制來處理,所以應該調用 方法返回 ServeltOutputStream 對象來向客戶端寫入文件內容。
文件下載操作步驟
ListFileServlet——>listfiles.jsp——>DownloadServlet.java
1、 ListFileServlet.java
a) 得到文件保存目錄的真是路徑
b) 創建方法:void listFiles(File file,Map map),迭代處理所有目錄及文件,將所有文件存入Map對象中,K——V uuidname——realname
c) 將Map對象寫入request,傳至listfiles.jsp
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//得到保存上傳文件的文件夾
String savepath = this.getServletContext().
getRealPath("/WEB-INF/upload");
Map map = new HashMap();
//迭歸文件夾下面的所有文件 迭歸過程中如何保存迭歸出來的數據
listFiles(new File(savepath),map);
request.setAttribute("map", map);
request.getRequestDispatcher("/listfile.jsp").
forward(request, response);
}
private void listFiles(File file,Map map) {
if(file.isFile()){
String uuidname = file.getName();
String realname = uuidname.
substring(uuidname.indexOf("_")+1);
map.put(uuidname, realname);
}else{
//得到目錄下所有的文件
File files[] = file.listFiles();
for(File f: files){
listFiles(f,map);
}
}
}
2、 listfiles.jsp
取出map中的數據,並構建url作爲下載鏈接的href屬性值
<c:forEach var="me" items="${filesMap}">
<c:url var="fileAddr" value="/servlet/DownloadServlet">
<c:param name="uuidName">${me.key }</c:param>
<c:param name="realName">${me.value }</c:param>
</c:url>
文件名:${me.value }<a href="${fileAddr }" >下載</a><br/>
</c:forEach>
顯示結果如下
3、 DownloadServlet.java
a) 從request中取出文件名等信息,注意中文字符使用的urlencoding,需要進行手動編碼轉換。
b) 根據文件的uuidName計算出文件的存儲路徑
c) 判斷目標文件是否存在
d) 設置response的header中的content-disposition 值爲attachment;filename=realName
e) 獲取輸入流,並寫入輸出流
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String uuidName = request.getParameter("uuidName");
String realName = request.getParameter("realName");
uuidName = new String(uuidName.getBytes("iso8859-1"),"utf-8");
//真實文件名取出來必須經過轉碼,然後因爲需要作爲下載鏈接地址,所以需要再一次轉碼
realName = URLEncoder.encode(new String(realName.getBytes("iso8859-1"),"utf-8"),"utf-8");
System.out.println(getFileAddr(uuidName));
File file = new File(getFileAddr(uuidName));
if(!file.exists()){
request.setAttribute("message", "下載的文件不存在!");
request.getRequestDispatcher("/message.jsp")
.forward(request, response);
}else{
response.setHeader("content-disposition", "attachment;filename="+realName);
FileInputStream fin = new FileInputStream(file);
OutputStream out = response.getOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = fin.read(buffer))>0){
out.write(buffer,0,len);
}
fin.close();
}
}
public String getFileAddr(String uuidName){
int dir1 = uuidName.hashCode() & 0xf;
int dir2 = (uuidName.hashCode()>>4) & 0xf;
String fileAddr = this.getServletContext().getRealPath("/WEB-INF/upload")
+"\\"+ dir1+"\\" + dir2 +"\\" + uuidName;
return fileAddr;
}