深入分析JavaWeb Item47 -- Struts2攔截器與文件上傳下載
一、struts2中的攔截器(框架功能核心)
1、過濾器VS攔截器
過濾器VS攔截器功能是一回事。
過濾器是Servlet規範中的技術,能夠對請求和響應進行過濾。
攔截器是Struts2框架中的技術。實現AOP(面向切面)的編程思想。是可插拔的, 能夠對訪問某個 Action 方法之前或之後實施攔截。
攔截器棧(Interceptor Stack): 將攔截器按一定的順序聯結成一條鏈. 在訪問被攔截的方法時, Struts2攔截器鏈中的攔截器就會按其之前定義的順序被依次調用
Struts2運行原理 - 底層分析
2、自己定義攔截器
struts2定義了一個攔截器接口Interceptor接口。
Interceptor接口裏面有三個抽象方法
- init: 該方法將在攔截器被創建後馬上被調用, 它在攔截器的生命周期內僅僅被調用一次. 能夠在該方法中對相關資源進行必要的初始化
- interecept: 每攔截一個動作請求, 該方法就會被調用一次.
- destroy: 該方法將在攔截器被銷毀之前被調用, 它在攔截器的生命周期內也僅僅被調用一次.
Struts 會依次調用程序猿為某個 Action 而註冊的每個攔截器的 interecept 方法.每次調用 interecept 方法時, Struts 會傳遞一個 ActionInvocation 接口的實例.
ActionInvocation: 代表一個給定動作的運行狀態, 攔截器能夠從該類的對象裏獲得與該動作相關聯的 Action 對象和 Result 對象. 在完畢攔截器自己的任務之後, 攔截器將調用 ActionInvocation 對象的 invoke 方法前進到 Action 處理流程的下一個環節.
還能夠調用 ActionInvocation 對象的 addPreResultListener 方法給 ActionInvocation 對象 “掛” 上一個或多個 PreResultListener 監聽器. 該監聽器對象能夠在動作運行完畢之後, 開始運行動作結果之前做些事情
自己定義攔截器步驟:
a、編寫一個類。實現com.opensymphony.xwork2.interceptor.Interceptor接口,或者繼承
com.opensymphony.xwork2.interceptor.AbstractInterceptor類。(適配器模式),一般都選擇繼承AbstractInterceptor
由於AbstractInterceptor 類實現了 Interceptor 接口. 並為 init, destroy 提供了一個空白的實現
編寫兩個攔截器InterceptorDemo1 ,和InterceptorDemo2
package com.itheima.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class InterceptorDemo1 extends AbstractInterceptor {
//動作的每次訪問都會調用該方法
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("攔截前Demo1");
String rtvalue = invocation.invoke();//放行,這裏為什麽返回string?
由於終於的結果返回的Action的Result。而action的結果是string類型
System.out.println("攔截後Demo1");
return rtvalue;
}
}
package com.itheima.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
public class InterceptorDemo2 extends AbstractInterceptor {
//動作的每次訪問都會調用該方法
public String intercept(ActionInvocation invocation) throws Exception {
// invocation.addPreResultListener(new PreResultListener() {
//
// public void beforeResult(ActionInvocation invocation, String resultCode) {
// System.out.println("結果顯示前");
// }
// });
System.out.println("攔截前Demo2");
String rtvalue = invocation.invoke();//放行
System.out.println("攔截後Demo2");
return rtvalue;
}
}
b、須要在struts.xml中進行定義,定義攔截器,先定義在使用。
<package name="p1" extends="struts-default">
<!-- 定義攔截器:僅僅對當前包有效 -->
<interceptors>
<interceptor name="interceprotDemo1" class="com.itheima.interceptor.InterceptorDemo1"></interceptor>
<interceptor name="interceprotDemo2" class="com.itheima.interceptor.InterceptorDemo2"></interceptor>
</interceptors>
</package>
c、在動作配置中就能夠使用了
<action name="action1" class="com.itheima.action.Demo1Action" method="execute">
<!-- 使用定義的攔截器。如過沒有指定不論什麽的攔截器,默認使用default-stack棧中的全部攔截器。
一旦指定了不論什麽一個攔截器,默認的就無效了
-->
<interceptor-ref name="interceprotDemo1"></interceptor-ref>
<interceptor-ref name="interceprotDemo2"></interceptor-ref>
<result>/success.jsp</result>
</action>
實現動作類Demo1Action
package com.itheima.action;
import com.opensymphony.xwork2.ActionSupport;
public class Demo1Action extends ActionSupport {
@Override
public String execute() throws Exception {
System.out.println("execute運行了");
return SUCCESS;
}
}
運行結果
由於struts2中如文件上傳。數據驗證,封裝請求參數到action等功能都是由系統默認的defaultStack中的攔截器實現的,所以我們定義的攔截器須要引用系統默認的defaultStack,這樣應用才幹夠使用struts2框架提供的眾多功能。
如過沒有指定不論什麽的攔截器。默認使用default-stack棧中的全部攔截器;一旦指定了不論什麽一個攔截器,默認的就無效了除了要使用自己定義的攔截器之外,還要使用defaultStack,能夠這麽辦
方法一:(自己使用),僅僅需在action中配置自己定義的和defaultStack默認的就能夠了。
方法二:(大家都用的時候),假設希望包下的全部action都使用自己定義的攔截器, 要使用攔截器棧 interceptor-stack,定義一個interceptor-stack。然後在action中能夠通過<default-interceptor-ref name=“mydefaultStack”/>
把攔截器定義為默認攔截器,mydefaultStack名字能夠自己取。
<interceptors>
<interceptor name="interceprotDemo1" class="com.itheima.interceptor.InterceptorDemo1"></interceptor>
<interceptor name="interceprotDemo2" class="com.itheima.interceptor.InterceptorDemo2"></interceptor>
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="interceprotDemo1"></interceptor-ref>
<interceptor-ref name="interceprotDemo2"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="action3" class="com.itheima.action.LoginAction" method="login">
<interceptor-ref name="mydefaultStack"></interceptor-ref>
<result>/success.jsp</result>
</action>
3、Struts2 自帶的攔截器
案例1:檢查用戶是否登錄
1、 編寫頁面login.jsp
<body>
<form action="${pageContext.request.contextPath}/login.action" method="post">
<input type="text" name="username"/><br/>
<input type="text" name="password"/><br/>
<input type="submit" value="登錄"/>
</form>
</body>
2、編寫登錄校驗的攔截器LoginCheckInterceptor 類
package com.itheima.interceptor;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class LoginCheckInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
HttpSession session = ServletActionContext.getRequest().getSession();//通過ServletActionContext對象獲得session對象
Object user = session.getAttribute("user");
if(user==null){
//沒有登錄
return "login";//返回到某個邏輯視圖
}
return invocation.invoke();//放行
}
}
3、編寫配置文件struts.xml
<package name="p2" extends="struts-default">
<interceptors>
<interceptor name="loginCheckInterceptor" class="com.itheima.interceptor.LoginCheckInterceptor"></interceptor>
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="loginCheckInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="login" class="com.itheima.action.CustomerAction" method="login">
<result>/login.jsp</result>
</action>
</package>
4、編寫動作類CustomerAction
package com.itheima.action;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class CustomerAction extends ActionSupport {
public String login(){
System.out.println("登錄");
ServletActionContext.getRequest().getSession().setAttribute("user", "ppp");
return SUCCESS;
}
}
案例2:監測動作方法的運行效率
編寫時間監測過濾器TimerInterceptor
package com.itheima.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class TimerInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
long time = System.nanoTime();
String rtvalue = invocation.invoke();
System.out.println(rtvalue+"運行耗時:"+(System.nanoTime()-time)+"納秒");
return rtvalue;
}
}
編寫配置文件
<package name="p2" extends="struts-default">
<interceptors>
<interceptor name="loginCheckInterceptor" class="com.itheima.interceptor.LoginCheckInterceptor"></interceptor>
<interceptor name="timerInterceptor" class="com.itheima.interceptor.TimerInterceptor"></interceptor>
<interceptor-stack name="mydefaultStack">
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="loginCheckInterceptor"></interceptor-ref>
<interceptor-ref name="timerInterceptor"></interceptor-ref>
</interceptor-stack>
</interceptors>
<result name="login">/login.jsp</result>
</action>
</package>
從上面能夠看出。在一個action 中能夠配置多個過濾器。
4、自己定義攔截器:能夠指定攔截的方法或不攔截的方法
能夠指定攔截的方法或不攔截的方法。編寫過濾器時。能夠實現類MethodFilterInterceptor。裏面有兩個字段,通過註入參數就能夠指定那些不攔截。兩個參數僅僅要用一個就可以,當攔截較少是,能夠用includeMethods ,當攔截較多是。能夠用排除的方法excludeMethods 。
excludeMethods = Collections.emptySet();//排除那些
includeMethods = Collections.emptySet();//包含那些
案例:再續登錄校驗的樣例。
1、編寫過濾器LoginCheckInterceptor
package com.itheima.interceptor;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class LoginCheckInterceptor extends MethodFilterInterceptor {
protected String doIntercept(ActionInvocation invocation) throws Exception {
HttpSession session = ServletActionContext.getRequest().getSession();
Object user = session.getAttribute("user");
if(user==null){
//沒有登錄
return "login";//返回到某個邏輯視圖
}
return invocation.invoke();//放行
}
}
2、編寫配置文件
3、編寫動作類CustomerAction
package com.itheima.action;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class CustomerAction extends ActionSupport {
public String add(){
System.out.println("調用add的service方法");
return SUCCESS;
}
public String edit(){
System.out.println("調用edit的service方法");
return SUCCESS;
}
public String login(){
System.out.println("登錄");
ServletActionContext.getRequest().getSession().setAttribute("user", "ppp");
return SUCCESS;
}
}
4、編寫頁面
addCustomer.jsp
<body>
加入客戶
</body>
editCustomer.jsp
<body>
改動客戶
</body>
login.jsp
<body>
<form action="${pageContext.request.contextPath}/login.action" method="post">
<input type="text" name="username"/><br/>
<input type="text" name="password"/><br/>
<input type="submit" value="登錄"/>
</form>
</body>
success.jsp
<body>
oyeah
</body>
二、文件上傳與下載
Struts2開發的三板斧。頁面jsp—配置文件struts2.xml—-還有動作類Action
文件上傳前提:
form表單的method必須是post
form表單的enctype必須是multipart/form-data
提供type=”file”的上傳輸入域
Struts 對文件上傳的支持的一些規則
1、單文件上傳
開發步驟:
1、在WEB-INF/lib下加入commons-fileupload-1.2.1.jar、commons-io-1.3.2.jar。這兩個文件能夠從http://commons.apache.org/下載
2、第二步:編寫upfile.jsp ,把form表的enctype設置為:“multipart/form-data“,例如以下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<body>
<s:actionerror/>
<hr/>
<s:fielderror></s:fielderror>
<form action="${pageContext.request.contextPath}/upload1.action" method="post" enctype="multipart/form-data"><!-- 以MIME的方式傳遞
-->
用戶名:<input type="text" name="username"/><br/>
靚照:<input type="file" name="photo"/><br/>
<input type="submit" value="上傳"/>
</form>
</body>
編寫錯誤頁面error.jsp
<body>
server忙,一會再試。
</body>
success.jsp
<body>
上傳成功
</body>
3、編寫UploadAction1 類:在Action類中加入屬性,屬性相應於表單中文件字段的名稱:
package com.itheima.actions;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
//文件上傳:fileUpload攔截器完畢的
public class UploadAction1 extends ActionSupport {
private String username;
private File photo;//和表單的上傳字段名保持一致。類型是File類型的
private String photoFileName;//上傳的文件名稱
private String photoContentType;//上傳文件的MIME類型
//省略getter和setter方法
public String upload(){
System.out.println(photoFileName+":"+photoContentType);
//普通字段:
System.out.println(username);
//上傳字段:上傳到某個文件夾。存到應用的images文件夾下
String realPath = ServletActionContext.getServletContext().getRealPath("/images");
File directory = new File(realPath);
if(!directory.exists()){
directory.mkdirs();
}
try {
FileUtils.copyFile(photo, new File(directory, photoFileName));
return SUCCESS;
} catch (IOException e) {
e.printStackTrace();
return ERROR;
}
}
}
在struts.xml文件裏添加例如以下配置
<action name="upload1" class="com.itheima.actions.UploadAction1" method="upload">
<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedTypes">image/jpeg,image/png</param>
<param name="fileUpload.allowedExtensionsSet">jpg,jpeg,png</param>
</interceptor-ref>
<result>/success.jsp</result>
<result name="error">/error.jsp</result>
<result name="input">/index.jsp</result>
</action>
原理分析:
a 、FileUpload 攔截器負責處理文件的上傳操作, 它是默認的 defaultStack 攔截器棧的一員. 攔截器有 3 個屬性能夠設置.
- maximumSize: 上傳文件的最大長度(以字節為單位), 默認值為 2 MB
- allowedTypes: 同意上傳文件的類型, 各類型之間以逗號分隔
- allowedExtensions: 同意上傳文件擴展名, 各擴展名之間以逗號分隔
能夠在 struts.xml 文件裏覆蓋這 3 個屬性
b、超出大小或非法文件的上傳,會報錯(轉向一個input的視圖)
通過:
<s:actionError/> <s:feildError/>
顯示錯誤消息的提示
c、錯誤消息提示改為中文版:借助國際化的消息資源文件
假設是通過配置全局默認參數引起的錯誤。最好用全局的消息資源文件。
struts2默認的提示資源文件:struts2-core-**.jar
的org.apache.struts2的struts-message.properties文件裏。
比著key值覆蓋相應的value就可以。
配置例如以下:
struts.messages.error.uploading=Error uploading: {0}
struts.messages.error.file.too.large=File too large: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3}
{0}:<input type=“file” name=“uploadImage”>
中name屬性的值
{1}:上傳文件的真實名稱
{2}:上傳文件保存到暫時文件夾的名稱
{3}:上傳文件的類型(對struts.messages.error.file.too.large是上傳文件的大小)
源代碼:
改動顯示錯誤的資源文件的信息
第一步:創建新的資源文件 比如fileuploadmessage.properties,放置在src下
在該資源文件裏添加例如以下信息
struts.messages.error.uploading=上傳錯誤: {0}
struts.messages.error.file.too.large=上傳文件太大: {0} "{1}" "{2}" {3}
struts.messages.error.content.type.not.allowed=上傳文件的類型不同意: {0} "{1}" "{2}" {3}
struts.messages.error.file.extension.not.allowed=上傳文件的後綴名不同意: {0} "{1}" "{2}" {3}
第二步:在struts.xml文件載入該資源文件
<!-- 配置上傳文件的出錯信息的資源文件 -->
<constant name="struts.custom.i18n.resources" value=“cn….xxx.fileuploadmessage“/>
2、多文件上傳
上傳多個文件, 能夠使用數組或 List,其它和單文件上傳相似。
package com.itheima.actions;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
//文件上傳:fileUpload攔截器完畢的
public class UploadAction2 extends ActionSupport {
private String username;
private File[] photo;//和表單的上傳字段名保持一致。類型是File類型的 .數組或List
private String[] photoFileName;//上傳的文件名稱
private String[] photoContentType;//上傳文件的MIME類型
public String upload(){
//上傳字段:上傳到某個文件夾。存到應用的images文件夾下
String realPath = ServletActionContext.getServletContext().getRealPath("/images");
File directory = new File(realPath);
if(!directory.exists()){
directory.mkdirs();
}
try {
for(int i=0;i<photo.length;i++){
FileUtils.copyFile(photo[i], new File(directory, photoFileName[i]));
}
return SUCCESS;
} catch (IOException e) {
e.printStackTrace();
return ERROR;
}
}
}
3、文件下載
原理:struts2提供了stream結果類型,該結果類型就是專門用於支持文件下載功能的
指定stream結果類型 須要指定一個 inputName參數。該參數指定一個輸入流。提供被下載文件的入口
編碼步驟:
1、動作類DownloadAction :
package com.itheima.actions;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URLEncoder;
import org.apache.commons.io.FilenameUtils;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class DownloadAction extends ActionSupport {
private InputStream image;//用in有問題的
private String filename;//文件名稱
private long filesize;
public InputStream getImage() {
return image;
}
public void setImage(InputStream image) {
this.image = image;
}
public String getFilename() {
return filename;
}
public long getFilesize() {
return filesize;
}
public String download() throws Exception{
//給image字節流賦值
String fileRealPath = ServletActionContext.getServletContext().getRealPath("/WEB-INF/classes/黴女.jpg");
filename = FilenameUtils.getName(fileRealPath);
//方式一:中文文件要進行URL編碼
// filename = URLEncoder.encode(filename, "UTF-8");
filesize = new File(fileRealPath).length();
System.out.println(filename);
image = new FileInputStream(fileRealPath);
return SUCCESS;
}
}
struts.xml配置文件:主要是對stream類型的結果進行配置
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
<action name="download" class="com.itheima.actions.DownloadAction" method="download">
<result type="stream">
<param name="inputName">image</param><!--動作類中InputStream的字段名,須要在Action中提供getTargetFile方法,返回inputStream-->
<param name="contentType">application/octet-stream</param><!--告訴瀏覽器響應頭。文件的MIME格式,調用Action中的getContentType方法-->
<!-- 在struts.xml中使用OGNL表達式獲取動作類中屬性的值。 調用動作類中的 getFilename()-->
<!-- 中文文件名稱編碼:方式二.使用OGNL表達式,調用URLEncode的靜態方法 -->
<!-- 默認OGNL調用靜態方法是不行的,須要開啟一個常量開關.struts.ognl.allowStaticMethodAccess=true -->
<param name="contentDisposition">attachment;[email protected]@encode(filename,‘UTF-8‘)}</param><!-- 告訴瀏覽器的下載方式-->
<param name="contentLength">${filesize}</param>
</result>
</action>
</package>
</struts>
攔截器和文件上傳就寫到這裏了。好累!
深入分析JavaWeb Item47 -- Struts2攔截器與文件上傳下載