參考地址:http://commons.apache.org/fileupload
使用fileupload
根據你的應用需求,fileupload可以有許多不同的使用方式。在最簡單的情況下,你可以調用一個簡單的方法來解析servlet請求,然後在他們提交到你的應用時處理表單列表。在其它規模的終端上,你也可能決定將fileupload進行自定義,以完全控制個別表單項存儲的方法。比如,你或許會將內容流化來保存到數據中。
在這裏。我們將要描述fileupload的基本使用方法,然後解釋一些更簡單的,以及最常用的使用模式。fileupload的個性化在這裏得到了描述。
它怎麼工作
一個文件上傳請求由有序表單項的列表組成,這些表單是根據RFC1867來編碼的,是在html中基於表單的文件上傳。fileupload能解析這樣一個請求然後向你的應用提供一個單獨的上傳表單項的列表。每一個這樣的表單項實現了FileItem接口,而不需要考慮它潛在的實現方式。每個文件的表單項擁有一系列可能對你的應用有用的屬性,比如,每個項目都有一個名字和文件類型,你就可以提供一個InputStream來取它的數據。從另一方面說,你可能需要對這些選項進行不同的處理,這個可以根據對這個選項是不是一個規則的表單項來判斷,即可以根據這個數據是否來源於普通的文本框或者一個簡單的html表單域,還是一個能被上傳的文件。FileItem接口提供了多種方法來判斷它是否是一個可上傳的文件,然後你就可以用最合適的方式來處理這些數據了。FileUpload使用FileItemFactory來創建新的文件項目。這正是給FileUpload帶來靈活性的原因。這個工廠最終控制每個項目的創建。默認的工廠在內存或者磁盤上保存了項目的數據,這個可以根據項目的大小來定(例如,字節數據)。當然,這個動作可以通過自定義來滿足你應用的需要。
解析請求
在你對要上傳的選項處理之前,很顯然的你得先解析這些請求本身。很直接的就是要確保這個請求是不是一個要上傳的文件,然而FileUpload使這一點簡單化了,你只需提供一個靜態的方法來做到這一點。
//檢查是否是一個文件上傳請求
boolean isMultipart = FileUpload.isMultipartContent(request);
現在我們就可以準備解析這個請求到一個備選的選項了。解析的結果是一個文件選項的List,每個這樣的選項都實現了FileItem接口,處理這些選項將在下面得到討論。
最簡單的情況
最簡單的使用場景可以參照下面:
- 被上傳得選項必須以適度的大小駐留在內存中;
- 比較大的文件上傳選項必須寫入到磁盤的臨時文件中去;
- 大文件上傳請求必須不被允許;
- 默認的駐留內存的選項的最大大小,最大允許的上傳文件請求,和臨時文件的存儲地方是可以接受的;
在這種情景下處理這樣一個請求並不是很簡單的:
//創建一個新的文件上傳句柄
DiskFileUpload upload = new DiskFileUpload();
//解析請求
List /* FileItem */ items = upload.parseRequest(request);
這就是所有我們需要做的,真的!
解析的結果是一個文件項目的List,每一個都實現了FileItem接口。處理這些項目將在下面討論。
練習更多的控制
如果你的使用情景非常接近最簡單的使用方式,在上文中可以看到,但是你需要更多的控制臨界的大小和臨時文件的駐留地址,你可以使用DiskFileUpload類的方法來自定義這些動作,就像這樣:
//創建一個新的文件上傳句柄
DiskFileUpload upload = new DiskFileUpload();
//設置上傳參數
upload.setSizeThrehold(最大內存大小);
upload.setSizeMax(最大請求大小);
upload.setRepositoryPath(臨時目錄);
//解析請求
List /* FileItem */ items = upload.parseRequest(request);
當然,每個配置方法都是獨立於其它的,但是如果你想一次就配置它們,你可以使用可選的parseRequest()方法,像這樣:
// 建立一個新的文件上傳句柄
DiskFileUpload upload = new DiskFileUpload();
// 解析請求
List /* FileItem */ items = upload.parseRequest(request,
內存大小, 允許上傳的最大文件, 臨時目錄);
如果你想更多地控制請求的解析,比如把上傳選項存儲到其它地方,例如,存到數據庫中-你可以參照自定義FileUpload。
處理上傳選項
一旦解析過程完畢,你就可以獲得一個文件選項的List,以便進一步處理。在大多數情況下,你將會根據規則的表單域來不同地處理文件的上傳。所以你可能以這樣的方式來處理:
// 處理上傳的選項
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
processFormField(item);
} else {
processUploadedFile(item);
}
}
對於一個規則的表單域來說,你對它感興趣的可能就只有它的名字以及它的字符串值。你也會想到,處理它們是簡單的:
//處理一個規則表單域
if (item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
...
}
而對於一個文件的上傳,在你處理它的內容之前,可以有好多令你想知道的不同的東西,這裏有一個採用了一些你可能感興趣的方法的例子
// 處理一個文件上傳
if (!item.isFormField()) {
String fieldName = item.getFieldName();
String fileName = item.getName();
String contentType = item.getContentType();
boolean isInMemory = item.isInMemory();
long sizeInBytes = item.getSize();
...
}
對於這些上傳的文件,你一般不想通過內存來存取它們,除非它們很小,或者你沒有其它好的方法,更進一步,你想將內容當作文件流來處理,或者將整個文件寫到最終的地址。FileUpload提供了簡單的方法來完成這些 操作。
// 處理一個文件上傳的情況
if (writeToFile) {
File uploadedFile = new File(...);
item.write(uploadedFile);
} else {
InputStream uploadedStream = item.getInputStream();
...
uploadedStream.close();
}
注意到,在默認的FileUpload的實現中,write()方法將嘗試把文件改名以將它保存到特定的地點,如果數據已經在臨時文件中了,如果重命名失敗,實際的複製文件就完成了(?),在其它原因看來,或者數據已經在內存中了。如果你的確需要在內存中取上傳的數據,你只需簡單的調用get()方法來把它當作一個字符數組來獲得。
// 在內存中處理一個上傳的文件
byte[] data = item.get();
...
和殺毒軟件的相互作用
當web 容器在運行時,而殺毒軟件又同時運行在同樣的系統上,這種情況下在應用中使用FileUpload容易導致一些很難預料的事情.這部分將描述一些你可能要遇到的情況,我們會提供一些方法來處理它們。默認的FileUpload實現將會使超過它在內存中大小的上傳的選項寫入到磁盤。而當這樣的文件關閉後,任何系統中的殺毒軟件都會被喚醒,然後去檢查它,然後會潛在地隔離這個文件--就是說,將它移動到一個不產生問題的特定地方。這樣一來,對開發者來說當然是一個意外,因爲剛剛上傳的文件將不能被處理了。從另一方面來說,小於設定的內存大小的那些上傳的文件將被保持在內存中,這樣一來,將不會被殺毒軟件所檢測到,這樣就有可能使病毒以某種方式駐留在了系統中了(雖然如果一旦它被寫入到磁盤,殺毒軟件就會定位並檢測到它)。一個通用的解決方法是在系統中專門設置一個目錄來存放這些上傳的文件,然後配置殺毒軟件忽略這個目錄。這樣將確保上傳的文件在系統中不被隔離,但是這樣就把掃描病毒的責任交給了應用程序的開發者了。掃描這些上傳的文件的任務可以在外部的處理中實現。這樣可以將乾淨的文件移動到一個“改進”過的地方,或者也可以把殺毒集成到應用中去。至於怎麼將外部處理或集成病毒掃描到一個
應用,這個已經超出了本文檔的討論範圍。
下一步是什麼
希望這個頁面能提供給你一個好的意見,讓你在你自己的應用中能使用FileUpload。更多關於這裏介紹的方法,以及其它可用的方法,你可以參照api文檔。這裏介紹的用法已經可以滿足大多數的文件上傳的需要了,當然,如果你還有更多的複雜的需求,使用它的靈活的自定義配置的能力,FileUpload一定可以能夠幫助你。
一個簡單的示例:
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class FileUploadServlet extends HttpServlet {
public FileUploadServlet() {
super();
}
public void destroy() {
super.destroy(); // Just puts "destroy" string in log
// Put your code here
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
final long MAX_SIZE = 3 * 1024 * 1024;// 設置上傳文件最大爲 3M
// 允許上傳的文件格式的列表
final String[] allowedExt = new String[] { "jpg", "jpeg", "gif", "txt",
"doc", "docx", "mp3", "wma", "m4a" };
response.setContentType("text/html");
// 設置字符編碼爲UTF-8, 這樣支持漢字顯示
response.setCharacterEncoding("UTF-8");
// 實例化一個硬盤文件工廠,用來配置上傳組件ServletFileUpload
DiskFileItemFactory dfif = new DiskFileItemFactory();
dfif.setSizeThreshold(4096);// 設置上傳文件時用於臨時存放文件的內存大小,這裏是4K.多於的部分將臨時存在硬盤
dfif.setRepository(new File(request.getRealPath("/")
+ "ImagesUploadTemp"));// 設置存放臨時文件的目錄,web根目錄下的ImagesUploadTemp目錄
// 用以上工廠實例化上傳組件
ServletFileUpload sfu = new ServletFileUpload(dfif);
// 設置最大上傳尺寸
sfu.setSizeMax(MAX_SIZE);
PrintWriter out = response.getWriter();
// 從request得到 所有 上傳域的列表
List fileList = null;
try {
fileList = sfu.parseRequest(request);
} catch (FileUploadException e) {// 處理文件尺寸過大異常
if (e instanceof SizeLimitExceededException) {
out.println("文件尺寸超過規定大小:" + MAX_SIZE + "字節<p />");
out.println("<a href=/"upload.html/" target=/"_top/">返回</a>");
return;
}
e.printStackTrace();
}
// 沒有文件上傳
if (fileList == null || fileList.size() == 0) {
out.println("請選擇上傳文件<p />");
out.println("<a href=/"upload.html/" target=/"_top/">返回</a>");
return;
}
// 得到所有上傳的文件
Iterator fileItr = fileList.iterator();
// 循環處理所有文件
while (fileItr.hasNext()) {
FileItem fileItem = null;
String path = null;
long size = 0;
// 得到當前文件
fileItem = (FileItem) fileItr.next();
// 忽略簡單form字段而不是上傳域的文件域(<input type="text" />等)
if (fileItem == null || fileItem.isFormField()) {
continue;
}
// 得到文件的完整路徑
path = fileItem.getName();
// 得到文件的大小
size = fileItem.getSize();
if ("".equals(path) || size == 0) {
out.println("請選擇上傳文件<p />");
out.println("<a href=/"upload.html/" target=/"_top/">返回</a>");
return;
}
// 得到去除路徑的文件名
String t_name = path.substring(path.lastIndexOf("//") + 1);
// 得到文件的擴展名(無擴展名時將得到全名)
String t_ext = t_name.substring(t_name.lastIndexOf(".") + 1);
// 拒絕接受規定文件格式之外的文件類型
int allowFlag = 0;
int allowedExtCount = allowedExt.length;
for (; allowFlag < allowedExtCount; allowFlag++) {
if (allowedExt[allowFlag].equals(t_ext))
break;
}
if (allowFlag == allowedExtCount) {
out.println("請上傳以下類型的文件<p />");
for (allowFlag = 0; allowFlag < allowedExtCount; allowFlag++)
out.println("*." + allowedExt[allowFlag]
+ " ");
out.println("<p /><a href=/"upload.html/" target=/"_top/">返回</a>");
return;
}
long now = System.currentTimeMillis();
// 根據系統時間生成上傳後保存的文件名
String prefix = String.valueOf(now);
// 保存的最終文件完整路徑,保存在web根目錄下的ImagesUploaded目錄下
String u_name = request.getRealPath("/") + "ImagesUploaded/"
+ prefix + "." + t_ext;
try {
// 保存文件
fileItem.write(new File(u_name));
out.println("文件上傳成功. 已保存爲: " + prefix + "." + t_ext
+ " 文件大小: " + size + "字節<p />");
out.println("<a href=/"upload.html/" target=/"_top/">繼續上傳</a>");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void init() throws ServletException {
// Put your code here
}
}