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格式的圖片上傳將被過濾掉,並且後臺並不會報錯。