Struts2文件的上傳、下載之二


在開發基於Web的應用的時候,經常會碰到需要實現文件上傳、下載的功能,比如編輯某個商品,需要給它上傳一個圖片等等。

不使用Struts2的話,可以有很多種方式來實現文件上傳,比如使用Apache的Common-FileUpload等。但是這些傳統的方式,實現起來非常麻煩,需要寫很多代碼來進行控制。

現在使用Struts2來實現文件上傳的功能,會更加簡單和方便,事實上,Struts2的文件上傳功能,默認就是基於Common-FileUpload來實現的,只不過比直接使用Common-FileUpload來得更簡單。

1.2使用Struts2實現文件上傳

1:fileUpload攔截器

       使用Struts2來實現文件上傳,需要使用Struts2預定義的fileUpload攔截器。簡要的看看在struts-default.xml中的配置,示例如下:

 

java代碼:
  1. <package name="struts-default" abstract="true">  
  2.     …  
  3.     <interceptors>  
  4.         …  
  5.         <interceptor name="fileUpload"  
  6. ss="org.apache.struts2.interceptor.FileUploadInterceptor"/>  
  7.         …  
  8.         <interceptor-stack name="defaultStack">  
  9.             …  
  10.             <interceptor-ref name="fileUpload"/>  
  11.             …  
  12.         </interceptor-stack>  
  13.    </interceptors>  
  14.   
  15.    <default-interceptor-ref name="defaultStack"/>  
  16. </package>  

上面列出了struts-default.xml中跟fileUpload攔截器有關的部分。首先,Struts2有一個預定義攔截器叫fileUpload;然後,defaultStack攔截器棧引用了fileUpload攔截器;最後,struts-default包又將defaultStack攔截器聲明爲自己的默認攔截器引用。

因此,如果我們的包繼承了struts-default包,而我們的Action類又沒有聲明自己的攔截器引用的話,使用的就是defaultStack攔截器棧,從而引用了fileUpload攔截器。

2:實現上傳頁面

       如果一個表單中,包含了文件上傳的表單域,則一定要把整個表單enctype屬性設置爲multipart/form-data。形如:

 

java代碼:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <form action="/helloworld/uploadAction.action" method="post"   
  12.     enctype="multipart/form-data">  
  13.     文件名稱:<input type="text" name="fileName"><br>  
  14.     文件:<input type="file" name="myFile"><br>  
  15.     <input type="submit" value="提交">  
  16. </form>  
  17. </body>  
  18. </html>  

在這個文件裏,指定了<form>元素的enctype屬性爲multipart/form-data,還用了一個type爲file的輸入域,它會在頁面上生成一個文件選擇框。整個頁面運行如下:

圖13.1 文件上傳頁面

在這個頁面上文件域裏面,在輸入框後面多了一個“瀏覽”按鈕,點擊這個瀏覽按鈕,就會彈出文件選擇框。

3:實現文件上傳Action

       這個Action和其他的Action沒有太多特殊之處,使用一個String屬性fileName來準備接收前一個頁面的fileName文本框的值,然後使用一個File屬性myFile來準備接收前一個頁面的myFile文件框傳過來的文件的值。

在execute方法中,我們可以隨意存儲上傳的文件,本例只是把它放到了e盤的temp目錄下,並以傳入的fileName作爲保存的文件名。

示例代碼如下:

 

java代碼:
  1. public class UploadAction extends ActionSupport{  
  2.     private String fileName;  
  3.     private File myFile;  
  4.       
  5.     public String getFileName() {  
  6.         return fileName;  
  7.     }  
  8.     public void setFileName(String fileName) {  
  9.         this.fileName = fileName;  
  10.     }  
  11.     public File getMyFile() {  
  12.         return myFile;  
  13.     }  
  14.     public void setMyFile(File myFile) {  
  15.         this.myFile = myFile;  
  16.     }  
  17.       
  18.     public String execute()throws Exception{  
  19.         //先把上傳過來的文件存放到e盤temp目錄下,以傳入的fileName爲名字  
  20.         OutputStream output = null;  
  21.         InputStream input = null;  
  22.         try{  
  23.             output = new FileOutputStream("e:/temp/"+fileName);  
  24.             //建立一個1k大小的緩衝區  
  25.             byte[] bs = new byte[1024];  
  26.               
  27.             //將上傳過來的文件輸出到output中  
  28.             input = new FileInputStream(myFile);  
  29.             int length = 0;  
  30.             //length=input.read(bs)這句話中,length=-1代表了讀到文件結尾  
  31.             while ((length=input.read(bs))>0){  
  32.                 output.write(bs, 0, length);  
  33.             }  
  34.         }finally{  
  35.             input.close();  
  36.             output.close();  
  37.         }         
  38.         return SUCCESS;  
  39.     }  
  40. }  

注意:如何操作上傳的文件,完全是I/O的知識,爲了保持實例的簡單,只是採用了最簡單的方式,把內容存放在一個絕對路徑表示的文件裏面,當然還可以將上傳的文件存儲在web服務器上,甚至數據庫中。

4:映射這個Action

       在struts.xml中對這個Action進行配置,可以看到沒有任何特異之處,只是指出了Action名稱和對應的實現類,以及上傳之後要跳轉到的頁面。

 

java代碼:
  1. <package name="helloworld" extends="struts-default">  
  2.     <action name="uploadAction" class="cn.javass.fileupload.UploadAction">  
  3.         <result>/fileupload/success.jsp</result>  
  4.     </action>  
  5. </package>  

5:上傳之後的頁面

       由於上傳的文件在Action中已經被正確處理了,所以這個上傳之後的頁面沒有任何特殊的作用,只是顯示一下,表示上傳成功了,示例代碼如下:

 

java代碼:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <%@taglib prefix="s" uri="/struts-tags" %>  
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  5.  "http://www.w3.org/TR/html4/loose.dtd">  
  6. <html>  
  7. <head>  
  8. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  9. <title>Insert title here</title>  
  10. </head>  
  11. <body>  
  12.     成功上傳文件<s:property value="fileName"/>  
  13. </body>  
  14. </html>  

運行測試一下,在文件上傳頁面選擇要上傳的文件,然後點擊提交按鈕,會發現文件會上傳,並以我們指定的文件名稱保存到我們指定的文件夾下去了。

13.1.3獲取文件的更多信息

Struts2支持用更多的屬性來獲取有關上傳文件的真實文件名和文件類型。

比如:表單中包含了一個叫xyz的文件域,也就是類似於<input type=”file” name=”xyz”,那麼可以用三個屬性來獲取上傳文件的信息:

  • File類型的屬性xyz用來獲取文件內容。
  • String類型的屬性xyzFileName用來獲取文件的真實文件名。
  • String類型的屬性xyzContentType屬性用來獲取文件的類型。

將處理上傳文件的Action類修改一下,添加兩個屬性:myFileFileName和myFileContentType,以及他們對應的getter/setter方法,來嘗試獲得文件的真實文件名和文件類型。

另外,保存文件的時候,也使用文件的真實名稱,示例代碼如下:

 

java代碼:
  1. public class UploadAction extends ActionSupport{  
  2.     private String fileName;  
  3.     private File myFile;  
  4.     private String myFileFileName;  
  5.     private String myFileContentType;  
  6.     public String getMyFileFileName() {  
  7.         return myFileFileName;  
  8.     }  
  9.     public void setMyFileFileName(String myFileFileName) {  
  10.         this.myFileFileName = myFileFileName;  
  11.     }  
  12.     public String getMyFileContentType() {  
  13.         return myFileContentType;  
  14.     }  
  15.     public void setMyFileContentType(String myFileContentType) {  
  16.         this.myFileContentType = myFileContentType;  
  17.     }     
  18.     public String getFileName() {  
  19.         return fileName;  
  20.     }  
  21.     public void setFileName(String fileName) {  
  22.         this.fileName = fileName;  
  23.     }  
  24.     public File getMyFile() {  
  25.         return myFile;  
  26.     }  
  27.     public void setMyFile(File myFile) {  
  28.         this.myFile = myFile;  
  29.     }  
  30.       
  31.     public String execute()throws Exception{  
  32.         System.out.println("用戶傳入的文件名:"+fileName);  
  33.         System.out.println("上傳文件的真實文件名:"+myFileFileName);  
  34.         System.out.println("上傳文件的類型:"+myFileContentType);  
  35.         System.out.println("這個文件的文件名是什麼?"+myFile.getName());  
  36.   
  37.         //把上傳過來的文件存放到e盤temp目錄下,以真實的文件名作爲名字  
  38.         OutputStream output = null;  
  39.         InputStream input = null;  
  40.         try{  
  41.             output = new FileOutputStream("e:/temp/"+myFileFileName);  
  42.             //建立一個1k大小的緩衝區  
  43.             byte[] bs = new byte[1024];  
  44.               
  45.             //將上傳過來的文件輸出到output中  
  46.             input = new FileInputStream(myFile);  
  47.             int length = 0;  
  48.             //length=input.read(bs)這句話中,length=-1代表了讀到文件結尾  
  49.             while ((length=input.read(bs))>0){  
  50.                 output.write(bs, 0, length);  
  51.             }  
  52.         }finally{  
  53.             input.close();  
  54.             output.close();  
  55.         }         
  56.         return SUCCESS;  
  57.     }  
  58. }  

運行測試一下,後臺輸出爲:

 

java代碼:
  1. 用戶傳入的文件名:aaa  
  2. 上傳文件的真實文件名:filelist.xml  
  3. 上傳文件的類型:text/xml  
  4. 這個文件的文件名是什麼?upload__5ab75acc_12c5f17ac7a__8000_00000001.tmp  

注意其中“這個文件的文件名是什麼?”,代碼寫的是輸出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攔截器棧之前再引用一次,然後在這次引用上指定參數,示例代碼如下:

 

java代碼:
  1. <package name="helloworld" extends="struts-default">  
  2.     <action name="uploadAction" class="cn.javass.fileupload.UploadAction">  
  3.         <interceptor-ref name="fileUpload">  
  4.             <param name="allowedTypes">text/plain</param>  
  5.             <param name="maximumSize">1000</param>  
  6.         </interceptor-ref>  
  7.         <interceptor-ref name="defaultStack"/>  
  8.           
  9.         <result name="input">/fileupload/fileupload.jsp</result>  
  10.         <result>/fileupload/success.jsp</result>  
  11.     </action>  
  12. </package>  

觀察以上的<action>元素,要額外注意兩點:

  • 這個Action應該先引用fileUpload攔截器,然後引用defaultStack攔截器棧。
  • 這個Action中還要有一個叫input的<result>,以備上傳不成功時,Struts2跳轉到提交前的頁面。

還要稍微修改一下login.jsp,來顯示不滿足限制條件時的錯誤信息。只需要在合適的地方加上<s:fielderror/>來顯示錯誤信息即可。形如:

 

java代碼:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <%@taglib prefix="s" uri="/struts-tags" %>  
  12. <s:fielderror/>  
  13. <form action="/helloworld/uploadAction.action" method="post"   
  14.     enctype="multipart/form-data">  
  15.     文件名稱:<input type="text" name="fileName"><br>  
  16.     文件:<input type="file" name="myFile"><br>  
  17.     <input type="submit" value="提交">  
  18. </form>  
  19. </body>  
  20. </html>  

運行修改之後的文件上傳功能,如果上傳非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文件裏面,配置如下:

 

java代碼:
  1. struts.multipart.maxSize=2097152  

先來測試一下,看看是不是有這個問題。到文件上傳頁面,選擇一個超過上述字節數的文件來上傳看看,點擊提交按鈕,後臺輸出錯誤,示例如下:

 

java代碼:
  1. org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3402567) exceeds the configured maximum (2097152)  
  2.     at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:914)  
  3.     ......  

注意上面加粗的那句話,明確告訴我們,上傳的文件大小爲3402567,大於配置的最大值2097152字節。

       那麼該怎麼辦呢?

       方法很簡單,只要覆蓋這個默認設置就可以了,有兩種方式,一種是在Struts.properties裏面配置,另外一種就是在struts.xml裏面配置。這裏就以在struts.xml來配置爲例,只要在struts.xml中加入如下的常量定義,示例代碼如下:

 

java代碼:
  1. <constant name="struts.multipart.maxSize" value="10000000" />  

設置上傳文件的最大值爲大約10M。再次測試運行看看,應該就可以正常上傳了。

13.1.6在一個表單中上傳多個文件

在實際開發中,經常會碰到需要在一個表單中上傳多個文件的情況,比如爲某文檔添加附件,就可能要求在一個表單上添加多個附件。

對於在一個表單中上傳多個文件的功能,使用Struts2也能輕鬆實現。只需要在提交頁面上添加同名的多個文件輸入域,然後在Action中對應使用File類型的數組去接收這些參數即可。

       比如上面的示例,可以把上傳文件頁面改爲:

 

java代碼:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <%@taglib prefix="s" uri="/struts-tags" %>  
  12. <s:fielderror/>  
  13. <form action="/helloworld/uploadAction.action" method="post"   
  14.     enctype="multipart/form-data">  
  15.     文件:<input type="file" name="myFile"><br>  
  16.     文件:<input type="file" name="myFile"><br>  
  17.     文件:<input type="file" name="myFile"><br>  
  18.     文件:<input type="file" name="myFile"><br>  
  19.     <input type="submit" value="提交">  
  20. </form>  
  21. </body>  
  22. </html>  

上例中,<form>表單中有多個同名的file輸入域myFile。

       在Action中,只要使用數組或List來操作就可以了,這裏以數組爲例來修改Action,示例代碼如下:

 

java代碼:
  1. public class UploadAction extends ActionSupport{  
  2.     private File[] myFile;  
  3.     private String[] myFileFileName;  
  4.     public File[] getMyFile() {  
  5.         return myFile;  
  6.     }  
  7.     public void setMyFile(File[] myFile) {  
  8.         this.myFile = myFile;  
  9.     }  
  10.     public String[] getMyFileFileName() {  
  11.         return myFileFileName;  
  12.     }  
  13.     public void setMyFileFileName(String[] myFileFileName) {  
  14.         this.myFileFileName = myFileFileName;  
  15.     }  
  16.       
  17.     public String execute()throws Exception{  
  18.         //把上傳過來的文件存放到e盤temp目錄下,以真實的文件名作爲名字  
  19.         OutputStream output = null;  
  20.         InputStream input = null;  
  21.         try{  
  22.             for(int i=0;i<myFileFileName.length;i++){  
  23.                 output = new FileOutputStream("e:/temp/"+myFileFileName[i]);  
  24.                 //建立一個1k大小的緩衝區  
  25.                 byte[] bs = new byte[1024];  
  26.                   
  27.                 //將上傳過來的文件輸出到output中  
  28.                 input = new FileInputStream(myFile[i]);  
  29.                 int length = 0;  
  30.                 //length=input.read(bs)這句話中,length=-1代表了讀到文件結尾  
  31.                 while ((length=input.read(bs))>0){  
  32.                     output.write(bs, 0, length);  
  33.                 }  
  34.             }  
  35.         }finally{  
  36.             input.close();  
  37.             output.close();  
  38.         }         
  39.         return SUCCESS;  
  40.     }  
  41. }  

然後就可以去運行測試了,看看上面的實現是不是真的能同時上傳多個文件。但是要提醒一點,如果想要上傳不同類型的文件,記得把前面示例的時候,在struts.xml中所作的限制設定去掉。

發佈了23 篇原創文章 · 獲贊 2 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章