Java Web開發人員可以使用Apache檔案上傳元件來接收浏覽器上傳的檔案,該元件由多個類共同組成,
但是,對于使用該元件來編寫檔案上傳功能的Java Web開發人員來說,隻需要了解和使用其中的
三個類:DiskFileUpload、FileItem和FileUploadException。這三個類全部位于org.apache.commons.fileupload包中。
首先需要說明一下form表格的enctpye的屬性:
表單中enctype="multipart/form-data"的意思,是設定表單的MIME編碼。預設情況,這個編碼格式是application/x-www-form-urlencoded,不能用于檔案上傳;隻有使用了multipart/form-data,才能完整的傳遞檔案資料,
進行下面的操作.
enctype="multipart/form-data"是上傳二進制資料; form裡面的input的值以2進制的方式傳過去。
form裡面的input的值以2進制的方式傳過去,是以request就得不到值了。 也就是說加了這段代碼,用request就會傳遞不成功。
DiskFileUpload類
DiskFileUpload類是Apache檔案上傳元件的核心類,應用程式開發人員通過這個類來與Apache檔案上傳元件進行互動。
下面介紹DiskFileUpload類中的幾個常用的重要方法。
1.setSizeMax方法
setSizeMax方法用于設定請求消息實體内容的最大允許大小,以防止用戶端故意通過上傳特大的檔案來塞滿伺服器端的存儲空間,機關為位元組。
其完整文法定義如下:
public void setSizeMax(long sizeMax)
如果請求消息中的實體内容的大小超過了setSizeMax方法的設定值,該方法将會抛出FileUploadException異常。
2.setSizeThreshold方法
Apache檔案上傳元件在解析和處理上傳資料中的每個字段内容時,需要臨時儲存解析出的資料。
因為Java虛拟機預設可以使用的記憶體空間是有限的(筆者測試不大于100M),超出限制時将會發生“java.lang.OutOfMemoryError”錯誤,
如果上傳的檔案很大,例如上傳800M的檔案,在記憶體中将無法儲存該檔案内容,Apache檔案上傳元件将用臨時檔案來儲存這些資料;
但如果上傳的檔案很小,例如上傳600個位元組的檔案,顯然将其直接儲存在記憶體中更加有效。
setSizeThreshold方法用于設定是否使用臨時檔案儲存解析出的資料的那個臨界值,該方法傳入的參數的機關是位元組。其完整文法定義如下:
public void setSizeThreshold(int sizeThreshold)
3. setRepositoryPath方法
setRepositoryPath方法用于設定setSizeThreshold方法中提到的臨時檔案的存放目錄,這裡要求使用絕對路徑。其完整文法定義如下:
public void setRepositoryPath(String repositoryPath)
如果不設定存放路徑,那麼臨時檔案将被儲存在"java.io.tmpdir"這個JVM環境屬性所指定的目錄中,tomcat 5.5.9将這個屬性設定為了“<tomcat安裝目錄>/temp/”目錄。
4. parseRequest方法
parseRequest 方法是DiskFileUpload類的重要方法,它是對HTTP請求消息進行解析的入口方法,
如果請求消息中的實體内容的類型不是“multipart/form-data”,該方法将抛出FileUploadException異常。
parseRequest 方法解析出FORM表單中的每個字段的資料,并将它們分别包裝成獨立的FileItem對象,然後将這些FileItem對象加入進一個List類型的集合對象中傳回。parseRequest 方法的完整文法定義如下:
public List parseRequest(HttpServletRequest req)
parseRequest 方法還有一個重載方法,該方法集中處理上述所有方法的功能,其完整文法定義如下:
parseRequest(HttpServletRequest req,int sizeThreshold,long sizeMax,
String path)
這兩個parseRequest方法都會抛出FileUploadException異常。
5. isMultipartContent方法
isMultipartContent方法方法用于判斷請求消息中的内容是否是“multipart/form-data”類型,是則傳回true,否則傳回false。
isMultipartContent方法是一個靜态方法,不用建立DiskFileUpload類的執行個體對象即可被調用,其完整文法定義如下:
public static final boolean isMultipartContent(HttpServletRequest req)
6. setHeaderEncoding方法
由于浏覽器在送出FORM表單時,會将普通表單中填寫的文本内容傳遞給伺服器,對于檔案上傳字段,除了傳遞原始的檔案内容外,還要傳遞其檔案路徑名等資訊,
如後面的圖1.3所示。不管FORM表單采用的是“application/x-www-form-urlencoded”編碼,還是“multipart/form-data”編碼,
它們僅僅是将各個FORM表單字段元素内容組織到一起的一種格式,而這些内容又是由某種字元集編碼來表示的。
關于浏覽器采用何種字元集來編碼FORM表單字段中的内容,請參看筆者編著的《深入體驗java Web開發内幕——核心基礎》一書中的第6.9.2的講解,
“multipart/form-data”類型的表單為表單字段内容選擇字元集編碼的原理和方式與“application/x-www-form-urlencoded”類型的表單是相同的。
FORM表單中填寫的文本内容和檔案上傳字段中的檔案路徑名在記憶體中就是它們的某種字元集編碼的位元組數組形式,Apache檔案上傳元件在讀取這些内容時,
必須知道它們所采用的字元集編碼,才能将它們轉換成正确的字元文本傳回。
對于浏覽器上傳給WEB伺服器的各個表單字段的描述頭内容,Apache檔案上傳元件都需要将它們轉換成字元串形式傳回,
setHeaderEncoding 方法用于設定轉換時所使用的字元集編碼,其原理與筆者編著的《深入體驗java Web開發内幕——核心基礎》一書中的第6.9.4節講解的ServletRequest.setCharacterEncoding方法相同。
setHeaderEncoding 方法的完整文法定義如下:
public void setHeaderEncoding(String encoding)
其中,encoding參數用于指定将各個表單字段的描述頭内容轉換成字元串時所使用的字元集編碼。
注意:如果讀者在使用Apache檔案上傳元件時遇到了中文字元的亂碼問題,一般都是沒有正确調用setHeaderEncoding方法的原因。
FileItem類
FileItem類用來封裝單個表單字段元素的資料,一個表單字段元素對應一個FileItem對象,通過調用FileItem對象的方法可以獲得相關表單字段元素的資料。
FileItem是一個接口,在應用程式中使用的實際上是該接口一個實作類,該實作類的名稱并不重要,程式可以采用FileItem接口類型來對它進行引用和通路,為了便于講解,
這裡将FileItem實作類稱之為FileItem類。FileItem類還實作了Serializable接口,以支援序列化操作。
對于“multipart/form-data”類型的FORM表單,浏覽器上傳的實體内容中的每個表單字段元素的資料之間用字段分隔界線進行分割,兩個分隔界線間的内容稱為一個分區,
每個分區中的内容可以被看作兩部分,一部分是對表單字段元素進行描述的描述頭,另外一部是表單字段元素的主體内容,如圖1.3所示。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2ZugzZqVDMwUzMzEjMyMTMfBzLcRjMvwVMxETMwIzLcRnbl1GajFGd0F2LcRXZu5ibkN3YukGavw1LcpDc0RHaiojIsJye.gif)
圖1.3
主體部分有兩種可能性,要麼是使用者填寫的表單内容,要麼是檔案内容。FileItem類對象實際上就是對圖1.3中的一個分區的資料進行封裝的對象,它内部用了兩個成員變量來分别存儲描述頭和主體内容,其中儲存主體内容的變量是一個輸出流類型的對象。當主體内容的大小小于DiskFileUpload.setSizeThreshold方法設定的臨界值大小時,這個流對象關聯到一片記憶體,主體内容将會被儲存在記憶體中。當主體内容的資料超過DiskFileUpload.setSizeThreshold方法設定的臨界值大小時,這個流對象關聯到硬碟上的一個臨時檔案,主體内容将被儲存到該臨時檔案中。臨時檔案的存儲目錄由DiskFileUpload.setRepositoryPath方法設定,臨時檔案名的格式為“upload_00000005(八位或八位以上的數字).tmp”這種形式,FileItem類内部提供了維護臨時檔案名中的數值不重複的機制,以保證了臨時檔案名的唯一性。當應用程式将主體内容儲存到一個指定的檔案中時,或者在FileItem對象被垃圾回收器回收時,或者Java虛拟機結束時,Apache檔案上傳元件都會嘗試删除臨時檔案,以盡量保證臨時檔案能被及時清除。
下面介紹FileItem類中的幾個常用的方法:
1. isFormField方法
isFormField方法用于判斷FileItem類對象封裝的資料是否屬于一個普通表單字段,還是屬于一個檔案表單字段,如果是普通表單字段則傳回true,否則傳回false。該方法的完整文法定義如下:
public boolean isFormField()
2. getName方法
getName方法用于獲得檔案上傳字段中的檔案名,對于圖1.3中的第三個分區所示的描述頭,getName方法傳回的結果為字元串“C:\bg.gif”。如果FileItem類對象對應的是普通表單字段,getName方法将傳回null。即使使用者沒有通過網頁表單中的檔案字段傳遞任何檔案,但隻要設定了檔案表單字段的name屬性,浏覽器也會将檔案字段的資訊傳遞給伺服器,隻是檔案名和檔案内容部分都為空,但這個表單字段仍然對應一個FileItem對象,此時,getName方法傳回結果為空字元串"",讀者在調用Apache檔案上傳元件時要注意考慮這個情況。getName方法的完整文法定義如下:
public String getName()
注意:如果使用者使用Windows系統上傳檔案,浏覽器将傳遞該檔案的完整路徑,如果使用者使用Linux或者Unix系統上傳檔案,浏覽器将隻傳遞該檔案的名稱部分。
3.getFieldName方法
getFieldName方法用于傳回表單字段元素的name屬性值,也就是傳回圖1.3中的各個描述頭部分中的name屬性值,例如“name=p1”中的“p1”。getFieldName方法的完整文法定義如下:
public String getFieldName()
4. write方法
write方法用于将FileItem對象中儲存的主體内容儲存到某個指定的檔案中。如果FileItem對象中的主體内容是儲存在某個臨時檔案中,該方法順利完成後,臨時檔案有可能會被清除。該方法也可将普通表單字段内容寫入到一個檔案中,但它主要用途是将上傳的檔案内容儲存在本地檔案系統中。其完整文法定義如下:
public void write(File file)
5.getString方法
getString方法用于将FileItem對象中儲存的主體内容作為一個字元串傳回,它有兩個重載的定義形式:
public java.lang.String getString()
public java.lang.String getString(java.lang.String encoding)
throwsjava.io.UnsupportedEncodingException
前者使用預設的字元集編碼将主體内容轉換成字元串,後者使用參數指定的字元集編碼将主體内容轉換成字元串。如果在讀取普通表單字段元素的内容時出現了中文亂碼現象,請調用第二個getString方法,并為之傳遞正确的字元集編碼名稱。
6. getContentType方法
getContentType 方法用于獲得上傳檔案的類型,對于圖1.3中的第三個分區所示的描述頭,getContentType方法傳回的結果為字元串“image/gif”,即“Content-Type”字段的值部分。如果FileItem類對象對應的是普通表單字段,該方法将傳回null。getContentType 方法的完整文法定義如下:
public String getContentType()
7. isInMemory方法
isInMemory方法用來判斷FileItem類對象封裝的主體内容是存儲在記憶體中,還是存儲在臨時檔案中,如果存儲在記憶體中則傳回true,否則傳回false。其完整文法定義如下:
public boolean isInMemory()
8. delete方法
delete方法用來清空FileItem類對象中存放的主體内容,如果主體内容被儲存在臨時檔案中,delete方法将删除該臨時檔案。盡管Apache元件使用了多種方式來盡量及時清理臨時檔案,但系統出現異常時,仍有可能造成有的臨時檔案被永久儲存在了硬碟中。在有些情況下,可以調用這個方法來及時删除臨時檔案。其完整文法定義如下:
public void delete()
FileUploadException類
在檔案上傳過程中,可能發生各種各樣的異常,例如網絡中斷、資料丢失等等。為了對不同異常進行合适的處理,Apache檔案上傳元件還開發了四個異常類,其中FileUploadException是其他異常類的父類,其他幾個類隻是被間接調用的底層類,對于Apache元件調用人員來說,隻需對FileUploadException異常類進行捕獲和處理即可。