1. 程式人生 > >struts2.x多檔案上傳(使用註解)

struts2.x多檔案上傳(使用註解)

一般網站都會提供檔案的上傳與下載的功能,尤其是資料管理型網站。剛好在工作中需要用到,就提前學習了一下,並建了一個maven工程做練習。

1.      本工程使用maven建立工程,是為了省去包匯入細節,其中maven工程的pom.xml檔案主要如下:

<!-- struts2 core -->
<dependency>
     <groupId>org.apache.struts</groupId>
     <artifactId>struts2-core</artifactId>
     <version>2.3.16.3</version>
</dependency>
<!-- struts2註解包依賴,實現零配置 -->
<dependency>
     <groupId>org.apache.struts</groupId>
     <artifactId>struts2-convention-plugin</artifactId>
     <version>2.3.16.3</version>
</dependency>

引入struts2-convention-plugin,是為了使用註解,從而實現零配置,例項中將看到這個好處。仔細檢視struts2-core的pom.xml,可以發現如下依賴

<!-- File upload -->
<dependency>
     <groupId>commons-fileupload</groupId>
     <artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
     <groupId>commons-io</groupId>
     <artifactId>commons-io</artifactId>
</dependency>

可見,struts2.x已經預設使用Jakarta的Components-FileUpload元件作為其檔案上傳的框架。

2.       瞭解表單enctype屬性

在表單元素<form>中有一個屬性enctype是用來設定上傳資料的編碼方式,enctype屬性規定在傳送到伺服器之前瀏覽器應該對錶單資料進行何種編碼。預設的,表單資料會編碼為application/x-www-form-urlencoded。具體enctype有是3個值,含義具體如下

描述

application/x-www-form-urlencoded

在傳送前編碼所有字元(預設)

該種方式主要用於能輸出網頁的應用。當傳送的內容包含大量的非ASCII字元的文字或二進位制資料時,效率比較低

multipart/form-data

不對字元編碼。

在使用包含檔案上傳控制元件的表單時,必須使用該值。

該方式主要用於檔案上傳等應用,當利用該種方式上傳檔案時,首先會把資料轉化成二進位制資料,然後才會進行上傳

text/plain

空格轉換為 "+" 加號,但不對特殊字元編碼。該方式主要用於電子郵件方面的應用

從上述表中可以看出,只有把屬性enctype的值設定為multipart/form-data,就能實現檔案上傳。

3.       struts.xml配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
 
<struts>
    <!-- 常量配置-->
    <constant name="struts.convention.package.locators" value="web,action" />
    <constant name="struts.convention.package.locators.basePackage" value="com.ljx" />
    <constant name="struts.convention.action.name.lowercase" value="true" />
    <constant name="struts.convention.action.name.separator" value="-" />
    <constant name="struts.date.format" value="yyyy-MM-dd HH:mm:ss" />
    <!--<constant name="struts.convention.default.parent.package" value="ljx-default" />-->
    <constant name="struts.convention.result.path" value="/WEB-INF/views" />
    <constant name="struts.convention.action.includeJars" value=".*/struts*.*jar(!/)?" />
   
    <!-- 存放上傳檔案的臨時位置 -->
    <constant name="struts.multipart.maxSize" value="10701096"/>
    <constant name="struts.multipart.saveDir" value="D:/Download/tmp"/>
 
    <!-- 除錯模式 -->
   <constant name="struts.devMode" value="false" />
    <constant name="struts.convention.classes.reload" value="true" />
   
    <!-- 其餘的零配置 -->
 
</struts>

使用struts2-convention-plugin,只需要再struts.xml中配置一些常量值,其餘的元素<package>,<interceptor>等都不需要在這裡配置。

由簡到難,接下來先介紹單檔案上傳

4.       單檔案上傳

前端jsp檔案如下

<body background="${images}/bonjour-bg.jpg">
    <form name="file-upload-form" action="${ctx}/file/upload.action" method="post" enctype="multipart/form-data">
        <br/>
        選擇上傳的檔案:
        <input type="file" name="upload"/><br/><br/>
        <input type="submit" value="提交"/>
    </form>
</body>

備註,上傳檔案,form提交方式不能為Get,而應該為post。使用原生的html<input type=”file”>。題外話,因為struts-tag標籤有過安全漏洞,所以組裡開發嚴禁使用,樓主小生,不是特別理解xss漏洞原理,特別摘引了一篇大作(http://huaidan.org/archives/3433.html),留作後續研讀。當然,這裡只是作為輸入,所以應該使用<s:file>沒有太大問題的。

後臺Action如下:

/**
 * 檔案上傳
 *
 * @author linjx
 * @date 2014年7月17日  下午5:13:12
 * @version: 1.0.0
 */
@ParentPackage(value = "struts-default")
public class FileUploadAction {
   
    /**
     * 上傳的檔案
     */
    private File upload;
   
    /**
     * 檔名稱
     */
    private String uploadFileName;
   
    /**
     * 檔案型別
     */
    private String uploadContentType;
   
    @Action(value = "/file/upload")
    public String saveUploadFile() {
        //String realpath = ServletActionContext.getServletContext().getRealPath("/images"); //需要依賴javax.servlet.jar
        String realpath = "D:/download/download";  //指定儲存位置
        System.out.println("realpath: " + realpath);
        if (CommonUtils.isNotNull(upload)) {
            File saveFile = new File(new File(realpath), uploadFileName);
            if (!saveFile.getParentFile().exists()) {
                saveFile.getParentFile().mkdirs();
            }
            try {
                org.apache.commons.io.FileUtils.copyFile(upload, saveFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            ActionContext.getContext().put("message", "檔案上傳成功");
        }
        return ActionSupport.SUCCESS;
    }
 
    public File getUpload() {
        return upload;
    }
 
    public void setUpload(File upload) {
        this.upload = upload;
    }
 
    public String getUploadFileName() {
        return uploadFileName;
    }
 
    public void setUploadFileName(String uploadFileName) {
        this.uploadFileName = uploadFileName;
    }
 
    public String getUploadContentType() {
        return uploadContentType;
    }
 
    public void setUploadContentType(String uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
   
}

通過@ParenPackage和@Action註解,實現類似

<package name="file" extends="struts-default" namespace="/file ">
     <action name=" saveUploadFile " class="……. FileUploadAction ">
           <result>../upload.jsp</result>
           <result name="input">../file-upload.jsp</result>
     </action>
</package>

顯然,註解的優勢就是簡練。

結果返回upload.jsp程式碼如下:

<body background="${images}/bonjour-bg.jpg">
        <br/><br/>
        <h2>${message}</h2>
        <p>${uploadFileName}</p>
        <hr />
</body>

具體執行結果如下:


選定上傳檔案,點選提交,後臺報錯:

WARNING: Request exceeded size limit!
org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (29488656) exceeds the configured maximum (10701096)

非常直觀,是因為上傳檔案超過在struts.xml檔案中設定的上傳檔案最大值。我們選擇一個小檔案重新上傳,並觀察檔案上傳的流程,打除錯斷點於FileUtils.copyFile(upload, saveFile)行上,如下


可以發現,程式執行到這一步

D:/Download/tmp路徑下會有個臨時檔案,

繼續執行,則該臨時檔案會主動清除;檔案成功上傳到D:/Download/download資料夾下,檔名保持不變。

跳轉到upload.action,顯示如下

 

5.       多檔案上傳

       瞭解了單檔案上傳流程,就可以開始開發多檔案上傳。其實,沒有太大的差別,只是多檔案上傳在表單提交時,由檔案列表接收多個檔案,表單上傳後,會將多個檔案快取在臨時路徑,從操作上看,我們只需要將檔案列表拷貝到相應的路徑就行了。

具體程式碼如下:

上傳jsp

<body background="${images}/bonjour-bg.jpg">
        <form name="file-upload-form" action="${ctx}/file/multi-upload.action" method="post"
            enctype="multipart/form-data">
            <br/>
            選擇上傳的檔案:
            <br/>
            <input type="file" name="upload"/><br/>
            <input type="file" name="upload"/><br/>
            <input type="file" name="upload"/><br/>
            <input type="submit" value="提交"/>
        </form>
</body>

返回upload.jsp不變

Action程式碼如下:

/**
 * @author linjx
 * @date 2014-7-17
 * @version 1.0.0
 */
@ParentPackage(value = "struts-default")
public class MultiFileUploadAction {
   
    /**
     * 上傳的檔案
     */
    private List<File> upload;
   
    /**
     * 檔名稱
     */
    private List<String> uploadFileName;
   
    /**
     * 檔案型別
     */
    private List<String> uploadContentType;
   
    @Action(value = "/file/multi-upload", //view還是指向upload.jsp
            results = {@Result(name=ActionSupport.SUCCESS, location="/WEB-INF/views/file/upload.jsp")})
    public String saveUploadFile() {
        //需要javax.servlet.jar
        //String realpath = ServletActionContext.getServletContext().getRealPath("/images");
        String realpath = "D:/Download/download";
        System.out.println("realpath: " + realpath);
        if (CommonUtils.isNotNull(upload)) {
            if (CollectionUtils.isNotEmpty(upload)) {
                for (int i = 0; i < upload.size(); i ++) {
                    File saveFile = new File(realpath, uploadFileName.get(i));
                    if (!saveFile.getParentFile().exists()) {
                        saveFile.getParentFile().mkdirs();
                    }
                    try {
                        FileUtils.copyFile(upload.get(i), saveFile);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            ActionContext.getContext().put("message", "檔案上傳成功");
        }
        return ActionSupport.SUCCESS;
    }
 
    public List<File> getUpload() {
        return upload;
    }
 
    public void setUpload(List<File> upload) {
        this.upload = upload;
    }
 
    public List<String> getUploadFileName() {
        return uploadFileName;
    }
 
    public void setUploadFileName(List<String> uploadFileName) {
        this.uploadFileName = uploadFileName;
    }
 
    public List<String> getUploadContentType() {
        return uploadContentType;
    }
 
    public void setUploadContentType(List<String> uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
}

上傳結果如下,選定上傳檔案:


成功介面:


檢視download路徑下檔案有:


就此,完成多檔案上傳。

備註:還可以通過fileUpload攔截器對上傳的檔案進行過濾。

@InterceptorRef(value="fileUpload",params= {"allowedTypes", "image/bmp, /image/gif"})這個註解只限於類級別,如果加上這個過濾器,png格式的圖片上傳將被過濾掉,並且後臺並不會報錯。