Struts2 文件上傳

 

上傳單個文件

表單:

<s:form action="HandlerAction" method="POST" enctype="multipart/form-data">
    <s:file name="profile" label="頭像" />
<%-- <s:file name="profile" label="頭像" accept="image/jpeg" size="102400" /> --%>
    <s:submit value="提交" />
</s:form>
  • 須要設置 method="POST"    enctype="multipart/form-data"  。
  • 可使用struts2的表單標籤,也可使用html的表單標籤。html表單標籤支持的屬性要多一些,好比說html標籤支持multiple屬性。
  • accept、size屬性並不能限制上傳文件的類型、大小。accept只是在文件選擇器中指定文件類型,仍然能夠切換爲*(全部類型)。size屬性無效。

 

 

 

Action:

public class HandlerAction extends ActionSupport {
    private File profile;
    private String profileFileContentType;
    private String profileFileName;

    public File getProfile() {
        return profile;
    }

    public void setProfile(File profile) {
        this.profile = profile;
    }

    public String getProfileFileContentType() {
        return profileFileContentType;
    }

    public void setProfileFileContentType(String profileFileContentType) {
        this.profileFileContentType = profileFileContentType;
    }

    public String getProfileFileName() {
        return profileFileName;
    }

    public void setProfileFileName(String profileFileName) {
        this.profileFileName = profileFileName;
    }

    @Override
    public String execute() throws Exception {
        System.out.println("開始上傳");

        //取session中取出user,保存在user目錄下。getRealPath()取的是絕對路徑。
        String user = (String) ServletActionContext.getRequest().getSession().getAttribute("user");
        String dirPath = ServletActionContext.getServletContext().getRealPath("/upload") + "/" + user;

        File dir = new File(dirPath);
        if (!dir.exists()){
            dir.mkdirs();
        }

        File file = new File(dirPath + "/" + profileFileName);
        //將臨時文件保存到指定文件中(正式文件)
        profile.renameTo(file);
        //雖然gc每隔一段時間會自動清理,但使用完就刪除是最好的。
        profile.delete();

        System.out.println("上傳成功");
        return "success";
    }
}
  • 需提供一個File類型的成員變量,用於保存臨時文件。
  • 還須要提供2個對應的成員變量:FileContextType、FileName,用於保存文件類型、文件名,約定命名方式爲:File變量名+FileContextType | FileName。
  • 須要給這三個成員變量提供getter、setter方法。

 

 

此種方式只能上傳單個文件,不能一次上傳多個文件。html

此種方式不能上傳較大的文件,超過4M的文件就上傳不了。數組

 

 

 


 

 

 

限制上傳文件的類型、大小

在struts.xml中給處理文件上傳的action配置攔截器:服務器

<action name="HandlerAction" class="action.HandlerAction">
      <interceptor-ref name="fileUpload">
           <param name="allowedTypes">image/jpeg,image/png,image/gif</param>
           <!-- <param name="allowedExtensions">jpg,png,gif</param> -->
           <param name="maximumSize">102400</param>
      </interceptor-ref>
      <interceptor-ref name="defaultStack"></interceptor-ref>

     <result name="success">/success.jsp</result>
</action>

 注意:fileUpload攔截器的引用必須位於默認攔截器棧以前,不然能上傳文件,但起不到限制文件類型、大小的做用。session

 

 

 

原理分析

 鼠標點擊 <interceptor-ref name="fileUpload"> 中的fileUpload,Ctrl+B查看此攔截器的定義:併發

public class FileUploadInterceptor extends AbstractInterceptor {
    private static final long serialVersionUID = -4764627478894962478L;
    protected static final Logger LOG = LogManager.getLogger(FileUploadInterceptor.class);
    protected Long maximumSize;
    protected Set<String> allowedTypesSet = Collections.emptySet();
    protected Set<String> allowedExtensionsSet = Collections.emptySet();
    private ContentTypeMatcher matcher;
    private Container container;

    public FileUploadInterceptor() {
    }

 

 咱們在struts.xml中使用<param>傳入的文件類型、擴展名、最大尺寸這些參數,執行時會調用此攔截器類的setter方法,賦給對應的成員變量,再根據這些成員變量的值來檢測上傳文件是否符合要求。運維

 上面有一個成員變量 private ContentTypeMatcher matcher; 可用於檢測上傳文件的內容,好比檢測上傳的視頻是否包含色情畫面。dom

 

 

 

爲何fileUpload攔截器的引用必須位於默認攔截器棧以前?

此攔截器類中有一個static、final類型的成員變量:jsp

private static final long serialVersionUID = -4764627478894962478L;

這個UID是此攔截器類共有的,且初次賦值後不能再被修改,用於檢測此攔截器是否已執行。ide

上傳一個文件時,會先調用咱們配置的fileUpload攔截器來檢測文件是否符合要求,而後再調用默認的攔截器棧。默認的攔截器棧中有fileUpload攔截器。this

 

JVM先if判斷UID是否是long型的默認初始值:

  • 是:說明此攔截器還沒有被調用,則調用此攔截器
  • 不是:說明此攔截器已被調用過,則再也不調用此攔截器

若是默認攔截器棧在前,裏面的fileUplaod是未配置參數的,使用默認值,默認有大小限制,類型、擴展名能夠是任意的。

執行咱們本身配置的fileUplaod攔截器時,JVM一檢測UID,認爲此攔截器執行過了,直接跳過(就像過安檢,按流程走一次就ok,不會讓你再來一次),咱們本身配置的fileUpload是沒有起到做用的。

 

 

上傳多個文件時,上傳的每一個文件都會被fileUpload攔截器攔截(進站的每一個人都須要過安檢)。

 

若是不用限制上傳文件的類型、大小,則沒必要本身配置fileUpload攔截器,使用默認的攔截器棧就ok。

fileUpload攔截器是有默認參數的,若是本身沒有配置fileUpload攔截器的參數,默認上傳文件的最大尺寸爲4M,超過4M直接進入服務器報錯頁面。

 

 

 


 

 

 

 

設置上傳文件在服務器上的保存位置

把上傳文件在服務器上的保存位置寫死在代碼中,後續維護時若是要改保存位置,運維看不懂代碼,還得讓你來改,很麻煩。

 

一般的作法是:

  • 在處理上傳文件的action中設置一個成員變量,好比savePath,用來表示上傳文件的保存位置,並提供getter、setter方法
  • 在struts.xml配置action時,使用<param>傳入文件的保存位置
<action name="HandlerAction" class="action.HandlerAction">
     <!-- 注意路徑中的\要寫成\\或者/ --> <param name="savePath">D:/upload</param>
<interceptor-ref name="fileUpload"> <param name="allowedTypes">image/jpeg,image/png,image/gif</param> <param name="maximumSize">102400</param> </interceptor-ref> <interceptor-ref name="defaultStack"></interceptor-ref>
<result name="success">/success.jsp</result> </action>

 

 

 

 

 


 

 

 

 

同時上傳多個文件

表單

  • 同時指的是表單一次提交多個文件,能夠用多個<s:file />或<input type="file" />:
<s:form action="HandlerAction" method="POST" enctype="multipart/form-data">
    <s:file name="upload" label="上傳文件一" />
    <s:file name="upload" label="上傳文件二" />
    <s:submit value="提交" />
</s:form>

是把多個文件保存到一個數組中,一個文件即一個數組元素。注意name相同,都是action中的數組名。

 

  • 也能夠在一個文件選擇器中Ctrl選擇多個文件:
<input type="file" name="upload" multiple="multiple">

<s:file />沒有multiple屬性,一個<s:file />只能選擇1個文件,要一次性選擇多個文件,須要使用<input />。

 

  • 以上兩種方式能夠一塊兒使用。

 

 

 

action

public class HandlerAction extends ActionSupport {
    private File[] upload;
    private String[] uploadFileContentType;
    private String[] uploadFileName;

    public File[] getUpload() {
        return upload;
    }

    public void setUpload(File[] upload) {
        this.upload = upload;
    }

    public String[] getUploadFileContentType() {
        return uploadFileContentType;
    }

    public void setUploadFileContentType(String[] uploadFileContentType) {
        this.uploadFileContentType = uploadFileContentType;
    }

    public String[] getUploadFileName() {
        return uploadFileName;
    }

    public void setUploadFileName(String[] uploadFileName) {
        this.uploadFileName = uploadFileName;
    }

    private String savePath;

    public String getSavePath() {
        return savePath;
    }

    public void setSavePath(String savePath) {
        this.savePath = savePath;
    }


    @Override
    public String execute() throws Exception {
        File dir = new File(savePath);
        if (!dir.exists()){
            dir.mkdirs();
        }

        if(upload!=null) {
            for (int i = 0; i < upload.length; i++) {
                File file = new File(savePath + "/" + uploadFileName[i]);
                //將臨時文件保存到指定文件中(正式文件)
                upload[i].renameTo(file);
                //刪除臨時文件
                upload[i].delete();
                System.out.println(uploadFileName[i]+"上傳成功");

            }
        }

        return "success";
    }

}

換成數組罷了,處理時遍歷數組便可。

 

 

 

 

struts.xml

<action name="HandlerAction" class="action.HandlerAction">
            <param name="savePath">D:/upload</param>
            
            <interceptor-ref name="fileUpload">
                <param name="maximumSize">102400000</param>
            </interceptor-ref>
            <interceptor-ref name="defaultStack"></interceptor-ref>
            
            <result name="success">/success.jsp</result>
       <result name="input">/upload.jsp</result> </action>

使用input指定表單頁面,上傳出錯時回顯到表單頁面,並顯示錯誤信息。

 

 

 


 

 

 

防止上傳文件重名

經常使用的有2種方式

  • 在文件名中加時間戳(毫秒)。當併發量很大時,依然可能重名。
  • 使用UUID,即在原文件名上加UUID.randomUUID().toString() 

UUID即通用惟一識別碼,能惟一標識某個東西。

UUID產生的這個字符串包含32個十六進制數,用4根連詞線-分爲5段,示例: bd95572b-7fcf-46b2-ae3e-6087d66db40f ,8-4-4-4-12的形式

相關文章
相關標籤/搜索