1. Struts2文件上傳
1.1表單三要素
1)表單必須以POST方式提交,因為GET方式提交會有大小的限制(大約2000個字符),對POST,無大小限制要求。
2)表單必須加入enctype="multipart/form-data"表示上傳的文件,以隨著請求體,經過編碼後,一同傳入服務器。
3)每個表單項,必須取一個名字,因為服務端便於能過名字收集信息。
這裡使用Struts2的標簽:
<s:form action="upload"method="post"enctype="multipart/form-data">
<s:textfieldlabel="用戶名"name="username"/>
<s:filelabel="上傳文件"name="upload"/>
<s:submitvalue="提交"/>
</s:form>
1.2框架自身有fileUpload文件上傳攔截器
1)在默認棧中,有一個fileUpload文件上傳攔截器,無文件下載攔截器(要程序員自己處理)。org.apache.struts2.interceptor.FileUploadInterceptor(框架的攔截器)
2)在默認情況下,fileUpload攔截器只能上傳不超過2M(默認)的數據,而且struts2框架的上傳攔截器底層就是使用jakarta common fileupload開源組件來完成上傳的,和傳統Web應用上傳一樣。
1.3 fileUpload攔截器,自動注入三個參數
privateFile upload;//上傳臨時文件(fileUpload攔截器傳入的參數)
privateString uploadContentType;//上傳文件的類型(fileUpload攔截器傳入的參數)
privateString uploadFileName;//上傳文件的真實文件名(fileUpload攔截器傳入的參數)
注意:upload和表單項<s:file>中的name屬性一致;upload拼上 contentType 首字母c要大寫。
1.4上傳文件細節
1)上傳成功的時候臨時文件要刪除
//關閉資源,並刪除臨時文件,應在finally中處理的
if(is !=null) is.close();
if(os !=null) os.close();
if(upload.exists())upload.delete();
2)上傳文件總大小限制(上傳多個文件時加起來的大小)(框架默認是2M)
在struts.xml中配置:<constantname="struts.multipart.maxSize" value="20971520"/>
或在struts.properties中配置struts.multipart.maxSize=20971520
超過限制拋異常:
org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException
3)上傳文件單個大小限制
<!-- 設置[單]個上傳文件不得超過5M -->
<param name="maximumSize">5242880</param>
4)上傳文件擴展名限制
<!--設置上傳文件的擴展名-->
<paramname="allowedExtensions">.jpg,.txt</param>
5)上傳文件類型限制
<!--設置上傳文件的類型-->
<paramname="allowedTypes">image/pjpeg,text/plain</param>
<action name="UploadAction"class="com.maple.upload.action.UploadAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/upload_success.jsp</result>
<resultname="input"type="dispatcher">upload.jsp</result><!--有錯時轉到該頁面 -->
<!-- 給框架默認文件上傳攔截器(FileUploadInterceptor)設置值 -->
<interceptor-refname="fileUpload"><!-- name的值固定,可以在struts-default.xml文件中找到 -->
<!-- 設置上傳單個文件大小為5M -->
<param name="maximumSize">5242880</param>
<!-- 設置上傳文件的後綴名 -->
<param name="allowedExtensions">jpg,txt,doc,avi</param>
<!-- 設置上傳文件的內容類型 -->
<param name="allowedTypes">image/jpeg,text/plain,application/msword,video/x-msvideo</param>
</interceptor-ref>
<interceptor-refname="defaultStack"/><!--這個是默認的攔截器,如果申明了其他攔截器,必須顯式申明引用默認的 -->
</action>(如果沒有顯式引用則會報空指針,和JavaBean中必須有空參構造器類似)
1.5覆蓋系統的提示信息
寫一個任意.properies文件,存放的位置也是任意,在裡面覆蓋系統指定的key=value.
即覆蓋struts-messages.properties文件。
在struts.xml文件中,加入以下代碼,以至於讓框架在錯誤提示時,找用戶自定義的properties文件
<constant name="struts.custom.i18n.resources"value="配置文件的路徑 "/>
<!-- value值不需要後綴名,可以放多個,用英文逗號隔開 -->
<constant name="struts.custom.i18n.resources"value="my_message"/>value值的就是資源文件的路徑。
1.6上傳多個文件
private File[]upload;
private String[]uploadContentType;
private String[]uploadFileName;
收集參數時,可以使用數組,也可以使用集合List。
表單中的<s:file>標簽的name屬性值要一樣,這裡全為upload。
<s:form action="UploadAction"method="post"enctype="multipart/form-data">
<s:textfieldlabel="用戶名"name="username"/>
<s:filelabel="上傳文件1"name="upload"/>
<s:filelabel="上傳文件2"name="upload"/>
<s:filelabel="上傳文件3"name="upload"/>
<s:submitvalue="提交"/>
</s:form>
1.6將參數注入Action中
在XxxAction中,有個字段叫uploadPath,提供setter和getter方法。在struts.xml文件中可以為其賦值,在對應的action節點中添加param節點。如:
<action name="UploadAction"class="com.maple.upload.action.UploadAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/upload_success.jsp</result>
<!-- 為該action的uploadPath屬性注入值 -->
<paramname="uploadPath">D:/Java_Develop/My_Projects/MDay32/WebContent/WEB-INF/upload</param>
</action>
2.攔截器Interceptor
2.1概念
Struts2 攔截器在訪問某個 Action 方法之前或之後實施攔截, Struts2 攔截器是可插拔的。
攔截器棧(InterceptorStack): 將攔截器按一定的順序聯結成一條鏈. 在訪問被攔截的方法時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被依次調用,類似於Filter在web.xml文件中的配置順序調用。
每個攔截器都是實現了 com.opensymphony.xwork2.interceptor.Interceptor接口的Java 類。
init: 該方法將在攔截器被創建後立即被調用, 它在攔截器的生命周期內只被調用一次. 可以在該方法中對相關資源進行必要的初始化。
interecept: 每攔截一個動作請求,該方法就會被調用一次。
destroy: 該方法將在攔截器被銷毀之前被調用, 它在攔截器的生命周期內也只被調用一次。
2.2攔截器的作用
在struts2中,攔截器能夠對Action前後進行攔截,當請求某個action時執行攔截。
2.3自定義攔截器
1) 寫一個類,實現Interceptor接口
2) 在struts.xml文件中配置(注冊)欄截器
在package節點下注冊:
<interceptors>
<!-- 注冊自定義攔截器 -->
<interceptor name="LoginInterceptor"class="com.maple.interceptor.action.LoginInterceptor"/>
</interceptors>
在action節點下引用:(誰引用就攔截誰)
<!-- 引用自定義攔截器-->
<interceptor-refname="LoginInterceptor"/>
攔截器中各個方法的執行順序:
默認構造器(服務器啟動的時候執行,且在init方法之前)
init()方法(服務器啟動的時候執行,在構造方法之後執行)
destory()方法(服務器關閉的時候執行)
intercept()方法(每次請求被攔截的Action時都會執行)
publicclass LoginInterceptorimplements Interceptor {
public LoginInterceptor(){
System.out.println("默認構造器");
}
@Override
publicvoid init() {
System.out.println("init()方法");
}
@Override
public Stringintercept(ActionInvocation invocation)throws Exception {
System.out.println("intercept()方法");
//調用invoke方法放行請求
return invocation.invoke();
}
@Override
publicvoid destroy() {
System.out.println("destroy()方法");
}
}
2.4欄截器的執行順序
在struts.xml文件中,<interceptor-ref/>的引用順序一致,先引用的先執行,後引用的後執行。
如果某個攔截器出錯或不允許通過,那麼下一個攔截器無法執行
需要攔截哪個Action,就在哪個Action對應的<action>標簽中配置即可
在部署web應用時,攔截器的空參構造方法和init()方法各執行一次,每次請求時,intercept()都會執行一次 。
2.5攔截器棧
<interceptors>
<!-- 注冊自定義攔截器 -->
<interceptorname="LoginInterceptor"class="com.maple.interceptor.action.LoginInterceptor"/>
<!-- 申明自定義攔截器棧 -->
<interceptor-stackname="myStack">
<!-- 這個是默認的攔截器,如果申明了其他攔截器,必須顯式申明引用默認的 -->
<interceptor-refname="defaultStack"/>
<!-- 引用自定義的攔截器 -->
<interceptor-refname="LoginInterceptor"/>
</interceptor-stack>
</interceptors>
在Action節點中引用自定的攔截器棧:
<!-- 引用自定義的攔截器棧 -->
<interceptor-refname="myStack"/>
3.國際化
3.1國際化的概念
國際化(Internationalization由於在I和n之間有18個字母,所以也簡稱作I18N)是指對程序的某種處理使之能夠運行於不同的區域和語言環境中。一個國際化的軟件應該具有以下一些特征:
1、 能夠運行於不同區域和語言環境中;
2、 如果提供資源文件,可以即時增加對某種語言的支持,而且可以通過修改資源文件內容來定制自己的界面顯示;
3、 界面布局以及其它一些地域相關的信息顯示能夠符合該區域使用者的習慣;
4、 最重要的一點就是,你的應用程序必須能夠實實在在支持國際化使用者的需求,不僅僅是顯示的正確性,還包括數據的正確處理,操作的正確性。
提到國際化就不得不提本地化。本地化(Localization簡稱作L10N),就是調整軟件以適應於特定區域和語言環境的一個過程。可以說,國際化是本地化的一個前奏,一個良好國際化的軟件可以使其本地化工作事半功倍。比如,現在Windows中英文版本的發布幾乎能夠做到完全同步就歸功於最初設計時的國際化考慮。
不同國家的客戶,訪問同一張Web頁面,看到的文字效果不一樣。
3.2對JSP頁面國際化
國際化資源文件定義的格式:基名_語言_國家(可省略).properties
如:message_zh_CN.propreties message_en_US.propreties
默認資源文件 基名.properties
如:message.properties
步驟:
1. 創建login.jsp頁面
2. 創建資源文件
message_zh_CN.propreties message_en_US.propreties
2. 在struts.xml文件中package節點下配置:
<constantname="struts.custom.i18n.resources" value="message"/>
value的值只要寫資源的基名就可以了,不用後綴。
在頁面中使用<s:textfieldkey="login.username(資源文件中的key)"/>
<s:submit key="login.submit(資源文件中的key)"/>
為{0},{1}占位符動態設置值
<s:text name="資源文件的key"/>
<s:param>為{0}設置值的真實值</s:param>
<s:param>為{1}設置值的真實值</s:param>
<s:form action="LoginAction"method="post">
<s:textfieldkey="login.username"name="username"/>
<s:submitkey="login.submit"/>
</s:form>
<!--
login.hello=IAM {0}, YOU ARE {1}
login.jack=JACK
login.marry=MARRY
-->
<s:text name="login.hello">
<s:param><s:textname="login.jack"/></s:param><!--為占位符賦值 -->
<s:param><s:textname="login.marry"/></s:param>
</s:text>
3.3對驗證錯誤消息提取
寫死的錯誤提示信息:
publicvoid validate() {
if(StringUtils.isBlank(username)) {
this.addFieldError("username","用戶名不能為空");
}else {
//指定用戶名必須為中文
if(!username.matches("[\u4E00-\uFA29]+")) {
this.addFieldError("username","用戶名必須為中文");
}
}
}
配置的錯誤提示信息:
1)在國際化的資源文件中配置以下key–value
login.username.required =USERNAMEREQUIRED
login.username.mustchinese =USERNAMEMUSTBECHINESE
publicvoid validate() {
if(StringUtils.isBlank(username)) {
// this.addFieldError("username","用戶名不能為空");
/*
* getText方法會根據key到資源文件中找對應的值
* 是TextProvider接口的中方法
* ActionSupport實現了它
*/
this.addFieldError("username",this.getText("login.username.required"));
}else {
//指定用戶名必須為中文
if(!username.matches("[\u4E00-\uFA29]+")) {
// this.addFieldError("username","用戶名必須為中文");
this.addFieldError("username",this.getText("login.username.mustchinese"));
}
}
}
可以將出錯後的消息,綁定到資源文件中,有利於解耦
this.getText("result.username.mustchinese")
其中result.username.mustchinese是資源文件中的鍵,自動找對應的值。
4.結果類型
4.1常用結果集類型
Struts2的結果集類型有10種,在struts2-core-xxxx.jar下的struts-defalut.xml文件中約111行可以看到定義的結果集類型。
常用的有:
1) dispatcher 轉發 和servlet中的轉發一樣。
2) redirect 重定向 和servlet中的重定向一樣。
3) stream 流類型的結果,主要用於文件下載。
4) redirectAction 可以用於訪問某個Action中某個業務方法
如:http://.....//FromAction!execute.action(訪問FromAction 中的 execute方法)
重定向後浏覽器的地址欄會變化:如
http://localhost:8080/ToAction!execute.action(用!將action名和業務方法名隔開)
<actionname="FromAction"class="com.maple.result.action.FromAction"method="execute">
<!-- 通過指定type=redirectAction重定向到另一個Action中執行指定的方法 -->
<resultname="goToAction"type="redirectAction">
<!-- 指定要重定向到的Action的名字 -->
<param name="actionName">ToAction</param>
<!-- 指定ToAction的名稱空間,默認是/ -->
<param name="namespace">/</param>
<!-- 指定要執行哪個方法,默認是execute -->
<param name="method">execute</param>
<!-- 注意:三個參數的名字可以從其源碼(org.apache.struts2.dispatcher.ServletActionRedirectResult)中查找到,也可以查文檔。 -->
</result>
</action>
<action name="ToAction"class="com.maple.result.action.ToAction"method="execute">
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
</action>
4.2局部、全局結果類型
1)局部結果類型
在<action>標簽中定義的<result>
2)全局結果類型
在<package>標簽中的<global-results>標簽中定義的<result>
<!-- 定義全局結果集 -->
<global-results>
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
</global-results>
3)當局部和全局共同作用時,局部優先(類似java中的局部變量);當局部沒有配置時,則使用全局結果,項目中可以將每個<action>標簽中的相同的<result>配置成全局結果。
5.異常處理
1)局部異常
在<action>標簽中定義的<exception>
<actionname="ToAction"class="com.maple.result.action.ToAction"method="execute">
<!-- 局部結果類型 -->
<resultname="success"type="dispatcher">/WEB-INF/result_success.jsp</result>
<!-- 局部異常
result:為出現異常後要轉到的結果的名稱,和result標簽的name屬性值對應
exception:為要捕獲的異常[全類名]
子異常(父子同在時,子起決定作用)-->
<exception-mappingresult="sonException"exception="java.lang.ArithmeticException"/>
<!-- 父異常 -->
<exception-mappingresult="fatherException"exception="java.lang.Exception"/>
</action>
2)全局異常
在<package>標簽中的<global-exception-mappings>標簽中定義的<exception>
<!-- 定義全局異常 -->
<global-exception-mappings>
<exception-mappingresult="fatherException"exception="java.lang.Exception"/>
</global-exception-mappings>
3)當局部和全局共同作用時,局部優先;當局部沒有配置時,則使用全局結果,項目中可以將每個<action>標簽中的相同的<exception>配置成全局異常。
當父子異常都存在時,子異常起決定作用。
6.類型轉換器
6.1內置的類型轉換器
com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter(默認類型轉換器)
1)HTTP是沒有類型的概念,只能將表單的參數,以String或String[]的方式接收
struts2提供了內置基本類型轉換器,它能將String類型和8種基本類型自動轉換。
8種基本數據類型:byte char int short long float double boolean
com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter
2)struts2的內置類型轉換器,能夠將String類型轉換java.util.Date類型,
但Date類型的字符串必須是yyyy-MM-dd,而且能對輸入日期的字符串格式進行
合理的判段(如月份不會有13月,日不會有32日等等)
3)內置轉換器,不能將String類型轉換任意JavaBean(模型)類型。
6.2自定義類型轉換器
1)寫一個類結成StrutsTypeConverte類,重寫兩個方法convertFromString和convertToString。
2)在src/xwork-conversion.properties文件,配置如下內容
JavaBean的全類名=自定義轉換器的全類名
*在src下寫一個配置文件,名字必須為xwork-conversion.properties
* 內容為key=value
* 如:要轉換的JavaBean全類名=轉換器全類名
方法的執行順序:
AddressConverter()
convertFromString()
setAddress()
getAddress()
convertToString()
toString()
/**
* TODO自定義類型轉換器,用於將字符串轉換為Address類型
*要在src下寫一個配置文件,名字必須為xwork-conversion.properties
*內容為key=value
*如:要轉換的JavaBean全類名=轉換器全類名
*/
public class AddressConverter extends StrutsTypeConverter {
public AddressConverter(){
/*
* 默認構造函數在服務器啟動的時候就調用
* 但為什麼會連續調用兩次????(未解決)
* 自定義攔截器的也一樣???(未解決)
*/
System.out.println("AddressConverter()");
}
//第二個參數中存放了JavaBean對象的內容,以字符串的形式,有幾個JavaBean對象
//vaules中就有幾個值, toClass指明要講String轉成什麼類型。
public ObjectconvertFromString(Map context, String[] values, Class toClass) {
System.out.println("convertFromString()");
//將字符串轉成JavaBean
if(Address.class.equals(toClass)) {//判斷是否為要轉換的類型
Stringstr = values[0];//取出第一個參數
String[]strs = str.split("-");//表單提交的時候是用-分隔的
returnnew Address(strs[0],strs[1], strs[2]); //將轉換好的對象返回
}
returnnull; //如果不是要轉換的類型則返回 null
}
public StringconvertToString(Map context, Object o) {
System.out.println("convertToString()");
//將JavaBean轉成字符串,會調用JavaBean的toString方法
/*
* 如果o是要轉換的類型,則進行強制類型轉換
* 將o轉換為Address,再調用toString方法,返回其值
*/
if(oinstanceof Address)return((Address)o).toString();
returnnull;
}
}