在開發基於Web的應用的時候,經常會碰到需要實現文件上傳、下載的功能,比如編輯某個商品,需要給它上傳一個圖片等等。
不使用Struts2的話,可以有很多種方式來實現文件上傳,比如使用Apache的Common-FileUpload等。但是這些傳統的方式,實現起來非常麻煩,需要寫很多代碼來進行控制。
現在使用Struts2來實現文件上傳的功能,會更加簡單和方便,事實上,Struts2的文件上傳功能,默認就是基於Common-FileUpload來實現的,只不過比直接使用Common-FileUpload來得更簡單。
1.2使用Struts2實現文件上傳
1:fileUpload攔截器
使用Struts2來實現文件上傳,需要使用Struts2預定義的fileUpload攔截器。簡要的看看在struts-default.xml中的配置,示例如下:
上面列出了struts-default.xml中跟fileUpload攔截器有關的部分。首先,Struts2有一個預定義攔截器叫fileUpload;然後,defaultStack攔截器棧引用了fileUpload攔截器;最後,struts-default包又將defaultStack攔截器聲明爲自己的默認攔截器引用。
因此,如果我們的包繼承了struts-default包,而我們的Action類又沒有聲明自己的攔截器引用的話,使用的就是defaultStack攔截器棧,從而引用了fileUpload攔截器。
2:實現上傳頁面
如果一個表單中,包含了文件上傳的表單域,則一定要把整個表單enctype屬性設置爲multipart/form-data。形如:
在這個文件裏,指定了<form>元素的enctype屬性爲multipart/form-data,還用了一個type爲file的輸入域,它會在頁面上生成一個文件選擇框。整個頁面運行如下:
圖13.1 文件上傳頁面
在這個頁面上文件域裏面,在輸入框後面多了一個“瀏覽”按鈕,點擊這個瀏覽按鈕,就會彈出文件選擇框。
3:實現文件上傳Action
這個Action和其他的Action沒有太多特殊之處,使用一個String屬性fileName來準備接收前一個頁面的fileName文本框的值,然後使用一個File屬性myFile來準備接收前一個頁面的myFile文件框傳過來的文件的值。
在execute方法中,我們可以隨意存儲上傳的文件,本例只是把它放到了e盤的temp目錄下,並以傳入的fileName作爲保存的文件名。
示例代碼如下:
注意:如何操作上傳的文件,完全是I/O的知識,爲了保持實例的簡單,只是採用了最簡單的方式,把內容存放在一個絕對路徑表示的文件裏面,當然還可以將上傳的文件存儲在web服務器上,甚至數據庫中。
4:映射這個Action
在struts.xml中對這個Action進行配置,可以看到沒有任何特異之處,只是指出了Action名稱和對應的實現類,以及上傳之後要跳轉到的頁面。
5:上傳之後的頁面
由於上傳的文件在Action中已經被正確處理了,所以這個上傳之後的頁面沒有任何特殊的作用,只是顯示一下,表示上傳成功了,示例代碼如下:
運行測試一下,在文件上傳頁面選擇要上傳的文件,然後點擊提交按鈕,會發現文件會上傳,並以我們指定的文件名稱保存到我們指定的文件夾下去了。
13.1.3獲取文件的更多信息
Struts2支持用更多的屬性來獲取有關上傳文件的真實文件名和文件類型。
比如:表單中包含了一個叫xyz的文件域,也就是類似於<input type=”file” name=”xyz”,那麼可以用三個屬性來獲取上傳文件的信息:
- File類型的屬性xyz用來獲取文件內容。
- String類型的屬性xyzFileName用來獲取文件的真實文件名。
- String類型的屬性xyzContentType屬性用來獲取文件的類型。
將處理上傳文件的Action類修改一下,添加兩個屬性:myFileFileName和myFileContentType,以及他們對應的getter/setter方法,來嘗試獲得文件的真實文件名和文件類型。
另外,保存文件的時候,也使用文件的真實名稱,示例代碼如下:
運行測試一下,後臺輸出爲:
注意其中“這個文件的文件名是什麼?”,代碼寫的是輸出myFile這個File對象的名字,由於myFile對象封裝了上傳文件的內容,因而某些朋友就認爲這個文件就是我們上傳的文件,從而認爲它的getName方法就是返回的上傳的這個文件的名字。
這是錯誤的,這個myFile文件是Struts2在上傳過程中生成的臨時文件,它的內容與上傳文件相同,但是名字並不相同,它的名字是由Struts2臨時生成的,形如“upload__5ab75acc_12c5f17ac7a__8000_00000001.tmp”這樣的。
13.1.4限制文件的大小及類型
在文件上傳的時候,有可能需要對文件的大小和類型做出限制。Struts2支持直接在fileUpload攔截器上設置參數來進行限制。
在引用fileUpload攔截器的時候,可以指定三個參數(指定<param>子元素):
- allowedTypes:指定允許上傳的文件的類型,如果存在多種類型,以逗號隔開。注意:這裏添的不是文件的擴展名,而是對應的ContentType,如果不知道某種文件的ContentType可以先上傳一下試試,在後臺輸出ContentType來。
- maximumSize:指定允許上傳的文件的最大字節數。
- allowedExtensions:指定允許上傳的文件的擴展名。
如果上傳的文件不滿足以上的參數指定的條件,則會跳轉到一個叫input的<result>上,一般input都會指回到提交之前的頁面,也就是文件上傳頁面。
參數非常簡單,但是配置比較怪異,fileUpload攔截器雖然在defaultStack攔截器棧上已經引用了,但是還可以在defaultStack攔截器棧之前再引用一次,然後在這次引用上指定參數,示例代碼如下:
觀察以上的<action>元素,要額外注意兩點:
- 這個Action應該先引用fileUpload攔截器,然後引用defaultStack攔截器棧。
- 這個Action中還要有一個叫input的<result>,以備上傳不成功時,Struts2跳轉到提交前的頁面。
還要稍微修改一下login.jsp,來顯示不滿足限制條件時的錯誤信息。只需要在合適的地方加上<s:fielderror/>來顯示錯誤信息即可。形如:
運行修改之後的文件上傳功能,如果上傳非txt文件,運行結果如下:
圖13.2 fileUpload攔截器限制文件上傳類型
如果上傳大於1000字節的txt文件,運行結果如下:
圖13.3 fileUpload攔截器限制文件上傳大小
13.1.5上傳超大的文件
Struts2在實現文件上傳的時候,還有一個小問題,那就是默認上傳文件的大小是不能超過2097152字節的。這個配置在struts2-core-2.1.8.1.jar文件裏面,“\org\apache\struts2”文件夾下的default.properties文件裏面,配置如下:
先來測試一下,看看是不是有這個問題。到文件上傳頁面,選擇一個超過上述字節數的文件來上傳看看,點擊提交按鈕,後臺輸出錯誤,示例如下:
注意上面加粗的那句話,明確告訴我們,上傳的文件大小爲3402567,大於配置的最大值2097152字節。
那麼該怎麼辦呢?
方法很簡單,只要覆蓋這個默認設置就可以了,有兩種方式,一種是在Struts.properties裏面配置,另外一種就是在struts.xml裏面配置。這裏就以在struts.xml來配置爲例,只要在struts.xml中加入如下的常量定義,示例代碼如下:
設置上傳文件的最大值爲大約10M。再次測試運行看看,應該就可以正常上傳了。
13.1.6在一個表單中上傳多個文件
在實際開發中,經常會碰到需要在一個表單中上傳多個文件的情況,比如爲某文檔添加附件,就可能要求在一個表單上添加多個附件。
對於在一個表單中上傳多個文件的功能,使用Struts2也能輕鬆實現。只需要在提交頁面上添加同名的多個文件輸入域,然後在Action中對應使用File類型的數組去接收這些參數即可。
比如上面的示例,可以把上傳文件頁面改爲:
上例中,<form>表單中有多個同名的file輸入域myFile。
在Action中,只要使用數組或List來操作就可以了,這裏以數組爲例來修改Action,示例代碼如下:
然後就可以去運行測試了,看看上面的實現是不是真的能同時上傳多個文件。但是要提醒一點,如果想要上傳不同類型的文件,記得把前面示例的時候,在struts.xml中所作的限制設定去掉。