Java 文件上傳組件 Apache Commons FileUpload 應用指南(轉)

在最初的 http 協議中,沒有上傳文件方面的功能。RFC1867("Form-based File Upload in HTML".)
 http 協議添加了這個功能。客戶端的瀏覽器,如 Microsoft IE, Mozila, Opera 等,按照此規範將用
戶指定的文件發送到服務器。服務器端的網頁程序,如
 php, asp, jsp 等,能夠按照此規範,解析出用戶
發送來的文件。
php

2.1客戶端

簡單來講,RFC1867規範要求http協議增長了file類型的input標籤,用於瀏覽須要上傳的文件。同時
要求FORM表單的enctype屬性設置爲「multipart/form-data」,method屬性設置爲「post」便可,下面是咱們文
件上傳頁面的表單代碼:
html

<form action="<%=request.getContextPath()%>/servlet/SimpleUpload" enctype="multipart/form-data" 
method="post">java

文本1<input type="text" name="text1" value="文本1"><br>web

文件2<input type="text" name="text2" value="文本2"><br>數據庫

文件1<input type="file" name="file1"><br>apache

文件2<input type="file" name="file2"><br>編程

文件2<input type="file" name="file3"><br>api

<input type="submit" value="開始上傳">數組

</form>瀏覽器

2.2 服務器端

一個文件上傳請求的消息實體由一系列根據 RFC1867"Form-based File Upload in HTML".)編碼的項目
(文本參數和文件參數)組成。本身編程來解析獲取這些數據是很是麻煩的,還須要瞭解
RFC1867規範對請
求數據編碼的相關知識。
FileUpload 能夠幫助咱們解析這樣的請求,將每個項目封裝成一個實現了FileItem
接口的對象,並以列表的形式返回。因此,咱們只須要了解FileUploadAPI如何使用便可,不用管它們的底
層實現。讓咱們來看一個簡單文件上傳處理代碼:

DiskFileItemFactory factory = new DiskFileItemFactory();

ServletFileUpload uploader = new ServletFileUpload(factory);

List<FileItem> list = uploader.parseRequest(request);

if (item.isFormField()){

// 處理普通表單域

String field = item.getFieldName();//表單域名

String value = item.getString("GBK");

} else {

//將臨時文件保存到指定目錄

String fileName = item.getName();//文件名稱

String filepath = "您但願保存的目錄/" + fileName;

item.write(new File(filepath));//執行保存

}

    怎麼樣?簡單吧!下面咱們來繼續瞭解一些必須瞭解的API

FileItem接口

org.apache.commons.fileupload.disk.DiskFileItem實現了FileItem接口,用來封裝單個表單字段元素的
數據。經過調用
FileItem 定義的方法能夠得到相關表單字段元素的數據。咱們不須要關心DiskFileItem的具
體實現,在程序中能夠採用
FileItem接口類型來對DiskFileItem對象進行引用和訪問。FileItem類還實現了
Serializable接口,以支持序列化操做。

下圖是一個文件上傳表單:



上圖表單提交的http數據包的內容:

POST /demo/servlet/SimpleUpload HTTP/1.1

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/msword, 
application/vnd.ms-excel, application/vnd.ms-powerpoint, */*

Referer: http://127.0.0.1:8080/demo/simpleUpload.jsp

Accept-Language: zh-cn

Content-Type: multipart/form-data; boundary=---------------------------7da1772c5504c6

UA-CPU: x86

Accept-Encoding: gzip, deflate

User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727)

Host: 127.0.0.1:8080

Content-Length: 184423

Connection: Keep-Alive

Cache-Control: no-cache

Cookie: JSESSIONID=BD8E58E5BAD9B559C0262077FB5E0B4E

 

-----------------------------7da1772c5504c6

Content-Disposition: form-data; name="text1"

鄭州蜂鳥科技有限公司

-----------------------------7da1772c5504c6

Content-Disposition: form-data; name="text2"

申林

-----------------------------7da1772c5504c6

Content-Disposition: form-data; name="file1"; filename="C:\Documents and Settings\All Users\
Documents\My Pictures\示例圖片\Blue hills.jpg"

Content-Type: image/pjpeg

大量二進制數據內容,沒法複製 …….

-----------------------------7da1772c5504c6

Content-Disposition: form-data; name="file2"; filename="C:\Documents and Settings\All Users\
Documents\My Pictures\示例圖片\Sunset.jpg"

Content-Type: image/pjpeg

大量二進制數據內容,沒法複製 …….

-----------------------------7da1772c5504c6

Content-Disposition: form-data; name="file3"; filename="C:\Documents and Settings\All Users\
Documents\My Pictures\示例圖片\Water lilies.jpg"

Content-Type: image/pjpeg

大量二進制數據內容,沒法複製 …….

從第一行,也就是請求行,咱們能夠看出這是一個post請求。在請求頭部部分,咱們能夠看到這樣
一個頭部信息:

Content-Type: multipart/form-data; boundary=---------------------------7da1772c5504c6

其中紅色部分說明該請求是一個multipart/form-data類型即多媒體類型的請求。藍色部分boundary的值
定義了一個字段分隔界線。在消息體部分能夠看出每一個表單字段元素數據之間採用字段分隔界線進行分
割,兩個分隔界線間的內容稱爲一個分區,每一個分區中的內容包括兩部分,一部分是對錶單字段元素進
行描述的描述頭,另一部分是表單字段元素的主體內容。 

經過對比描述頭,咱們能夠很容易區分文本字段和文件字段。不論是文件字段仍是文本字段,都有
name屬性,即該字段做爲一個表單域的名字。而文件字段還有filename,即上傳文件自己的名字。另外,
還有
conten-type屬性用於指明文件的類型。

每個表單字段,無論它是文本仍是文件,都被封裝成 FileItem 對象,咱們稱之爲文件項,當文件
項數據內容尺寸小於
DiskFileItemFactory 的sizeThreshold 屬性設置的臨界值時,直接保存在內存中;不然,
將數據流以臨時文件的形式,保存在 DiskFileItemFactory 的 repository 屬性指定的臨時目錄中。臨時文件
名形如「
upload_00000005(八位或八位以上的數字).tmp」。FileItem類內部提供了維護臨時文件名中的
數值不重複的機制,以保證了臨時文件名的惟一性。另外,如何保證臨時文件能被及時清除,釋放寶貴
的系統資源,是很是重要的,咱們將在後面講解。 

FileItem類經常使用的方法:

1.  boolean isFormField()方法

isFormField方法用於判斷FileItem類對象封裝的數據是一個普通文本表單字段,仍是一個文件表單字
段,若是是普通表單字段則返回true,不然返回false

2.  String getName()方法 

getName方法用於得到文件上傳字段中的文件名,即表單字段元素描述頭中的filename屬性值,如「C:\Documents and Settings\All Users\Documents\My Pictures\示例圖片\Sunset.jpg」。若是FileItem類對象對
應的是普通表單字段,getName方法將返回null即便用戶沒有經過網頁表單中的文件字段傳遞任何
文件,但只要設置了文件表單字段的name屬性,瀏覽器也會將文件字段的信息傳遞給服務器,
只是文件名和文件內容部分都爲空,但這個表單字段仍然對應一個
FileItem對象,此時,
getName
方法返回結果爲空字符串"",讀者在調用Apache文件上傳組件時要注意考慮這個狀況。

注意:上面的數據包是經過IE提交,因此是完整的路徑和名稱。如 
C:\Documents and Settings\All Users\Documents\My Pictures\示例圖片\Sunset.jpg。若是是其它瀏覽
器,如火狐和Chromium,則僅僅是名字,沒有路徑,如Sunset.jpg。

3.  String getFieldName()方法

getFieldName方法用於返回表單字段元素描述頭的name屬性值,也是表單標籤name屬性的值。例
如「
name=file1」中的「file1」。

4.  void write(File file)方法

write方法用於將FileItem對象中保存的主體內容保存到某個指定的文件中。若是FileItem對象中的主
體內容是保存在某個臨時文件中,該方法順利完成後,臨時文件有可能會被清除。該方法也可將普通
表單字段內容寫入到一個文件中,但它主要用途是將上傳的文件內容保存在本地文件系統中。

5.  String getString()方法

getString方法用於將FileItem對象中保存的數據流內容以一個字符串返回,它有兩個重載的定義形式:

public java.lang.String getString()

public java.lang.String getString(java.lang.String encoding)

throws java.io.UnsupportedEncodingException

前者使用缺省的字符集編碼將主體內容轉換成字符串,後者使用參數指定的字符集編碼將主體內容
轉換成字符串。若是在讀取普通表單字段元素的內容時出現了中文亂碼現象,請調用第二個
getString方法,併爲之傳遞正確的字符集編碼名稱。

6.  String getContentType()方法

getContentType 方法用於得到上傳文件的類型,即表單字段元素描述頭屬性「Content-Type」的值,
如「
image/jpeg」。若是FileItem類對象對應的是普通表單字段,該方法將返回null

7.  boolean isInMemory()方法

isInMemory方法用來判斷FileItem對象封裝的數據內容是存儲在內存中,仍是存儲在臨時文件中,
若是存儲在內存中則返回true,不然返回false

8.  void delete()方法

delete方法用來清空FileItem類對象中存放的主體內容,若是主體內容被保存在臨時文件中,
delete方法將刪除該臨時文件。

儘管當FileItem對象被垃圾收集器收集時會自動清除臨時文件,但及時調用delete方法能夠更早的
清除臨時文件,釋放系統存儲資源。另外,當系統出現異常時,仍有可能形成有的臨時文件被永久
保存在了硬盤中。

9.  InputStream getInputStream()方法

    以流的形式返回上傳文件的數據內容。

10. long getSize()方法

返回該上傳文件的大小(以字節爲單位)。

DiskFileItemFactory類

將請求消息實體中的每個項目封裝成單獨的DiskFileItem (FileItem接口的實現對象的任務
由 
org.apache.commons.fileupload.FileItemFactory 接口的默認實現 
org.apache.commons.fileupload.disk.DiskFileItemFactory 來完成。當上傳的文件項目比較小時,直接保
存在內存中(速度比較快),比較大時,以臨時文件的形式,保存在磁盤臨時文件夾(雖然速度
慢些,可是內存資源是有限的)。

屬性

1) public static final int DEFAULT_SIZE_THRESHOLD :將文件保存在內存仍是
磁盤臨時文件夾的默認臨界值,值爲10240,即10kb

2) private File repository:用於配置在建立文件項目時,當文件項目大於臨界值時使
用的臨時文件夾,默認採用系統默認的臨時文件路徑,能夠經過系統屬性 java.io.tmpdir 
獲取。以下代碼:

System.getProperty("java.io.tmpdir");

3) private int sizeThreshold:用於保存將文件保存在內存仍是磁盤臨時文件夾的臨界值

構造方法

1) public DiskFileItemFactory():採用默認臨界值和系統臨時文件夾構造文件項工廠對象。

2) public DiskFileItemFactory(int sizeThreshold,File repository):採用參數指定臨界值和系統臨時
文件夾構造文件項工廠對象。

FileItem createItem() 方法

根據DiskFileItemFactory相關配置將每個請求消息實體項目建立 成DiskFileItem 實例,並返回。
該方法歷來不須要咱們親自調用,FileUpload組件在解析請求時內部使用。

void setSizeThreshold(int sizeThreshold)

Apache文件上傳組件在解析上傳數據中的每一個字段內容時,須要臨時保存解析出的數據,以便
在後面進行數據的進一步處理(保存在磁盤特定位置或插入數據庫)。由於
Java虛擬機默承認以使
用的內存空間是有限的,超出限制時將會拋出「
java.lang.OutOfMemoryError」錯誤。若是上傳的文件
很大,例如800M的文件,在內存中將沒法臨時保存該文件內容,Apache文件上傳組件轉而採用臨時
文件來保存這些數據;但若是上傳的文件很小,例如
600個字節的文件,顯然將其直接保存在內存中
性能會更加好些。

setSizeThreshold方法用於設置是否將上傳文件已臨時文件的形式保存在磁盤的臨界值(以字節
爲單位的
int值),若是從沒有調用該方法設置此臨界值,將會採用系統默認值10KB。對應的
getSizeThreshold() 方法用來獲取此臨界值。

void setRepository(File repository)

setRepositoryPath方法用於設置當上傳文件尺寸大於setSizeThreshold方法設置的臨界值時,將文件以
臨時文件形式保存在磁盤上的存放目錄。有一個對應的得到臨時文件夾的 File getRespository() 方法。

注意:當從沒有調用此方法設置臨時文件存儲目錄時,默認採用系統默認的臨時文件路徑,能夠
經過系統屬性 java.io.tmpdir 獲取。以下代碼:

System.getProperty("java.io.tmpdir");

Tomcat系統默認臨時目錄爲「<tomcat安裝目錄>/temp/」。

ServletFileUpload 類

org.apache.commons.fileupload.servlet.ServletFileUpload類是Apache文件上傳組件處理文件上傳的
核心高級類(所謂高級就是不須要管底層實現,暴露給用戶的簡單易用的接口)。

使用其 parseRequest(HttpServletRequest) 方法能夠將經過表單中每個HTML標籤提交的數據封裝
成一個
FileItem對象,而後以List列表的形式返回。使用該方法處理上傳文件簡單易用。

若是你但願進一步提升新能,你能夠採用 getItemIterator 方法,直接得到每個文件項的數據輸
入流,對數據作直接處理。

在使用ServletFileUpload對象解析請求時須要根據DiskFileItemFactory對象的屬性 sizeThreshold(臨
界值)和repository(臨時目錄) 來決定將解析獲得的數據保存在內存仍是臨時文件中,若是是臨時
文件,保存在哪一個臨時目錄中?。因此,咱們須要在進行解析工做前構造好DiskFileItemFactory對象,
經過ServletFileUpload對象的構造方法或setFileItemFactory()方法設置 ServletFileUpload對象的
fileItemFactory屬性。

ServletFileUpload繼承結構:

java.lang.Object

|—org.apache.commons.fileupload.FileUploadBase

     |—org.apache.commons.fileupload.FileUpload

|—org.apache.commons.fileupload.servlet.ServletFileUpload

構造方法:

1) public ServletFileUpload():構造一個未初始化的實例,須要在解析請求以前先調用
setFileItemFactory()方法設置 fileItemFactory屬性。

2) public ServletFileUpload(FileItemFactory fileItemFactory):構造一個實例,並根據參數
指定的FileItemFactory 對象,設置 fileItemFactory屬性。

ServletFileUpload類經常使用方法:

1. public void setSizeMax(long sizeMax)方法

setSizeMax方法繼承自FileUploadBase類,用於設置請求消息實體內容(即全部上傳數據)的最大
尺寸限制,以防止客戶端惡意上傳超大文件來浪費服務器端的存儲空間。其參數是以字節爲單位的
long型數字

在請求解析的過程當中,若是請求消息體內容的大小超過了setSizeMax方法的設置值,將會拋出
FileUploadBase內部定義的SizeLimitExceededException異常(FileUploadException的子類)。如:

org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: 
the request was rejected because its size (1649104) exceeds the configured 
maximum (153600)

該方法有一個對應的讀方法:public long getSizeMax()方法。

2. public void setFileSizeMax(long fileSizeMax)方法

setFileSizeMax方法繼承自FileUploadBase類,用於設置單個上傳文件的最大尺寸限制,以防止客戶
端惡意上傳超大文件來浪費服務器端的存儲空間。其參數是以字節爲單位的long型數字。該方法有一個
對應的讀方法:public long geFileSizeMax()方法。

在請求解析的過程當中,若是單個上傳文件的大小超過了setFileSizeMax方法的設置值,將會拋出
FileUploadBase內部定義的FileSizeLimitExceededException異常(FileUploadException的子類)。如:

org.apache.commons.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file1 exceeds its
 maximum permitted size of 51200 characters.

3. public List parseRequest(javax.servlet.http.HttpServletRequest req)

parseRequest 方法是ServletFileUpload類的重要方法,它是對HTTP請求消息體內容進行解析的入口
方法。它解析出FORM表單中的每一個字段的數據,並將它們分別包裝成獨立的FileItem對象,而後將這
些FileItem對象加入進一個List類型的集合對象中返回。

該方法拋出FileUploadException異常來處理諸如文件尺寸過大、請求消息中的實體內容的類型不
是「multipart/form-data」、IO異常、請求消息體長度信息丟失等各類異常。每一種異常都是
FileUploadException的一個子類型。

4. public FileItemIterator getItemIterator(HttpServletRequest request)

getItemIterator方法和parseRequest 方法基本相同。可是getItemIterator方法返回的是一個迭代
器,該迭代器中保存的不是
FileItem對象,而是FileItemStream 對象,若是你但願進一步提升新能,
你能夠採用 
getItemIterator 方法,直接得到每個文件項的數據輸入流,作底層處理;若是性能不
是問題,你但願代碼簡單,則採用
parseRequest方法便可。 

5. public stiatc boolean isMultipartContent(HttpServletRequest req)

isMultipartContent方法方法用於判斷請求消息中的內容是不是「multipart/form-data」類型,是則返
true,不然返回falseisMultipartContent方法是一個靜態方法,不用建立ServletFileUpload類的實例對
象便可被調用。

6. getFileItemFactory()setFileItemFactory(FileItemFactory)方法

方法繼承自FileUpload類,用於設置和讀取fileItemFactory屬性。

7. public void setProgressListener(ProgressListener pListener)

設置文件上傳進度監聽器。關於監聽器的具體內容,將在後面學習。該方法有一個對應的讀取
方法:ProgressListener getProgressListener()

8.public void setHeaderEncoding()方法

在文件上傳請求的消息體中,除了普通表單域的值是文本內容之外,文件上傳字段中的文件路
徑名也是文本,在內存中保存的是它們的某種字符集編碼的字節數組,Apache文件上傳組件在讀取
這些內容時,必須知道它們所採用的字符集編碼,才能將它們轉換成正確的字符文本返回。

setHeaderEncoding方法繼承自FileUploadBase類,用於設置上面提到的字符編碼。若是沒有設置,
則對應的讀方法getHeaderEncoding()方法返回null,將採用HttpServletRequest設置的字符編碼,若是
HttpServletRequest的字符編碼也爲null,則採用系統默認字符編碼。能夠經過一下語句得到系統默認
字符編碼:

System.getProperty("file.encoding"));

 

好,到這裏咱們學習了主要的一些API,足夠咱們來完成一個簡單文件上傳的功能了,下一章,
咱們將一塊兒來編寫一個文件上傳應用程序。

 

轉之:http://www.blogjava.net/whistler/articles/330976.html

相關文章
相關標籤/搜索