用文件上傳瞭解struts2攔截器的使用

struts2中,攔截器是非常核心的內容,框架默認提供的攔截器,我們可以從struts2-core-**.jar/struts-default.xml中查詢到,我們以struts2-core-2.3.15.3.jar爲例,打開struts-default.xml,可以看到如下的默認攔截器:

<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
<interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
<interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
<interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
<interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
<interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
<interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />

在struts-default.xml中,除了定義了一些默認的攔截器以外,還有一些默認的攔截器堆。這也提示我們,不光可以在使用時引用一個個的攔截器,也可以使用攔截器堆的方式,一次引用多個攔截器。如:我們在struts.xml配置文件中,沒有配置攔截器的情況下,會默認引用以下這個攔截器堆:

<interceptor-stack name="defaultStack">
    <interceptor-ref name="exception"/>
    <interceptor-ref name="alias"/>
    <interceptor-ref name="servletConfig"/>
    <interceptor-ref name="i18n"/>
    <interceptor-ref name="prepare"/>
    <interceptor-ref name="chain"/>
    <interceptor-ref name="scopedModelDriven"/>
    <interceptor-ref name="modelDriven"/>
    <interceptor-ref name="fileUpload"/>
    <interceptor-ref name="checkbox"/>
    <interceptor-ref name="multiselect"/>
    <interceptor-ref name="staticParams"/>
    <interceptor-ref name="actionMappingParams"/>
    <interceptor-ref name="params">
        <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
    </interceptor-ref>
    <interceptor-ref name="conversionError"/>
    <interceptor-ref name="validation">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="workflow">
        <param name="excludeMethods">input,back,cancel,browse</param>
    </interceptor-ref>
    <interceptor-ref name="debugging"/>
</interceptor-stack>

爲何我如此肯定,因爲在這個文件中,有以下代碼,我們在struts.xml中定義一個package元素時,通過也會extends這個struts-default.xml,那麼也表示我們引用這個攔截器堆:

<default-interceptor-ref name="defaultStack"/>

好了,抄了一陣struts的源碼,現在我們開始今天的事情:文件上傳

先來進行單文件上傳:

從上面的默認攔截器定義,可以看到,struts2給我們提供了一個現成的攔截器fileUpload,並且已經默認引用了。

我們先來做一些準備工作,做一個文件上傳的頁面,相信大家都還記得文件上傳的頁面必須滿足以下三個條件:

  1. 表單的提交方式必須是post
  2. 表單的enctype必須爲multipart/form-data
  3. 表單中必須提供一個type=file的input元素

以下就是這個表單提交的頁面:

    <fieldset>
        <legend>單文件上傳</legend>
        <s:form action="single/upload.action" enctype="multipart/form-data" method="post">
            <s:textfield name="name" label="姓名"></s:textfield>
            <s:file name="photo" label="靚照"></s:file>
            <s:submit value="上傳"></s:submit>
        </s:form>
    </fieldset>

上面使用的是struts2的標籤,當然也可以使用普通的html標籤。
上面的表單提交,我們需要一個action動作在來支持,我們先來在struts.xml中配置一下這個action動作

    <package name="upload" extends="struts-default" namespace="/single">
        <action name="upload" class="demo.action.SingleFileUploadAction" method="upload">
            <result>/success.jsp</result>
        </action>
    </package>

那個成功的返回頁面success.jsp,沒有任何展示,大不了就提示一下成功了

<body>
上傳成功了
</body>

現在,我們就只差一個動作類了,我們來寫配置文件中配置的demo.action.SingleFileUploadAction這個動作類:

package demo.action;

//省略引入的包

/**
 * 這個測試類,簡單起見,使用模型和動作爲同一個類的方式
 * @author Minhellic
 *
 */
public class SingleFileUploadAction extends ActionSupport {
    private String name; //對應頁面上的name字段
    private File photo;  //對應頁面上要上傳的文件,必須使用File來接收
    private String photoFileName; //上傳的文件名,XXXFileName這樣的固定寫法
    private String photoContentType; //上傳文件的MIME類型, XXXContentType這樣的固定寫法

    public String upload() throws IOException {
        //普通的屬性,可以直接使用
        System.out.println(name);

        /*
         * 完全文件上傳
         */
        //1.得到文件上要傳的真實路徑
        ServletContext sc = ServletActionContext.getServletContext();
        String dirPath = sc.getRealPath("/files");//得到文件的真實保存路徑,需要在WebRoot根目錄下建一個files目錄
        //2.構建目標文件
        File target = new File(dirPath, photoFileName);
        //3.複製文件
        FileUtils.copyFile(photo, target);

        return SUCCESS;
    }

    //省略各個屬性的getter和setter
}

以上代碼爲了簡潔,省略了導入的包和各個屬性的getter和setter,大家可以自己添加上。
通過上面的代碼可以看到,我們需要有一個文件夾來存放上傳的文件,本例,我們是在工程的根目錄下,用一個files文件夾來存放的,所以,我們在WebRoot下,建一個files文件夾

這裏寫圖片描述

好了,最最粗糙的文件上傳完成了,先來看看效果

部署到tomcat,訪問這個上傳頁面:

這裏寫圖片描述

隨便選張圖片,上傳下,果然,上傳成功了。我們到tomcat下去查看,在項目的根目錄下,發現有一個files目錄,打開這個目錄,發現確實上傳成功了

這裏寫圖片描述

感覺是完成了。再來得瑟一下,這回我選擇一個視頻,大概有幾十M吧,然後

這裏寫圖片描述

我操,這一定不是真的,剛纔玩得還好好的。關鍵是,控制檯還沒報錯,這怎麼辦?仔細看看這個報錯,發現報的是沒有input對應的邏輯視圖,可明明我們的action裏只返回了success,哪來的input,除了這個action,就只有一個可能,就是那個fileUpload攔截器乾的。
好吧,既然你說我沒有input視圖,那我給你一個,在struts.xml中,action的定義裏,添加一個result元素

<result name="input">/index.jsp</result>

再次去嘗試去上傳一個那個幾十M的視頻文件,這回倒是不報錯了,而是重新又回到了上傳頁面。
那麼問題來了,爲什麼呢?再上傳那張圖片是可以成功的,爲什麼視頻就不可以嗎?是因爲格式不對嗎?也沒有指定格式呀,按理說應該是可以支持所有格式的,畢竟只是一個上傳,又不是解析。
那會不會是大小的問題呢,可能性很大。因爲文件上傳不成功,無非不是路徑問題,文件格式問題,然後就是文件大小問題,前面已經成功過,那麼路徑是沒有問題的,格式的問題可能性很小。那麼我們來找找有沒有設置文件默認上傳大小的地方:
我們知道,struts2加載的時候,不單會加載struts-default.xml,還會加載一個叫default.properties的文件,我們在struts2-core-2.3.15.3.jar包中,找到這個文件,仔細一看,找到了:

struts.multipart.maxSize=2097152

原來這裏的文件默認大小這麼小,難怪上傳不上去,好吧,我們可以在struts.xml中,通過constant元素,來修改這個默認值

<constant name="struts.multipart.maxSize" value="100000000"></constant>

再次部署,試一下,終於成功了。在tomcat中,也找到了這個文件

這裏寫圖片描述

感覺像是應該沒有問題了。然而如果把這樣的上傳頁面交付給甲方,相信這項目一定就沒希望了。我們想想,這個地方明明在頁面上寫的是要上傳“靚照”的,那也就是說這裏我們希望要上傳的是一張圖片。可用戶可不管那麼多,他什麼都有可能上傳,就像我剛纔一樣,居然會上傳一個視頻上來。而且,如果用戶上傳的文件不對,應該給出提示。

那麼,下面我們就來限制一下用戶上傳文件的大小和後綴名,如果用戶上傳的東西不對,那麼給用戶一個提示。

文件大小的限制,上面已經介紹了,那麼現在,就應該是要給出用戶提示了。

爲了方便測試,我先把上傳大小的限制調小一些,這樣基本上隨便一個文件都會過大,這樣好測試:

<constant name="struts.multipart.maxSize" value="1000"></constant>

struts2提供了一些標籤,可以用於展示這些提示信息,比如這次的錯誤提示,我們就可以在頁面上用以下標籤展示:

<s:actionerror/>

這樣,我們再次上傳一個超過大小的文件,則會在頁面上出現以下提示:

這裏寫圖片描述

至於要把這個提示展示成中文的,則是可以通過國際化的配置來做,就不再是這篇博客要表現的東西了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章