作者:永恆の_☆ 地址:http://blog.csdn.net/chenghui0317/article/details/9531879
一、簡單介紹
使用Struts2上傳附件功能非常容易實現,只需要使用普通的Action類 用來獲取上傳的附件對象,然後保存到指定的路徑下即可。基本原理和Servlet上傳附件一樣,只不過封裝附件的方式實現不一樣罷了。servlet上傳附件的地址:http://blog.csdn.net/ch656409110/article/details/9502143
二、準備條件
準備一個struts2環境的項目
common-io.jar ,下載地址:http://commons.apache.org/io/download_io.cgi
common-upload.jar ,下載地址:http://jakarta.apache.org/commons/fileupload/
三、實現思路
準備一個上傳附件的頁面,代碼如下:
<form name="saveForm" action="${ctx}/house/save.action" method="post" οnsubmit="return checkSaveHouseForm();" enctype="multipart/form-data">
<tr>
<td class="field">上傳附件:</td>
<td>
<input type="file" name="upload" class="text" οnblur="checkUpload()"/>
<span id="sp_upload" class="message"></span>
<br />
</td>
</tr>
<tr>
<td class="field">附件名稱:</td>
<td>
<input type="text" name="fileName" class="text"/>
</td>
</tr>
<div class="buttons"><input type="submit" name="submit" value="發佈"/></div>
</form>
頁面中定義的附件控件名稱是 upload,那麼在Action類中也要定義相同的upload屬性,如下:
private File upload; //upload file
private String uploadFileName; //附件名稱
private String uploadContentType; //附件類型
private String savePath; //附件保存的路徑
private List<String> fileName ; //用戶輸入的名稱
//省略get set方法
需要注意的是,如果定義附件對象的附加屬性,要以附件對象爲前綴,比如upload爲名的附件。
並且savePath的get方法要去讀取才能獲得,代碼如下:
public synchronized String getSavePath() {
return ServletActionContext.getServletContext().getRealPath(savePath);
}
封裝好對象之後,接下來處理業務邏輯,具體代碼如下:
//處理上傳的附件
System.err.println(upload.getAbsolutePath());
System.err.println(upload.getName());
System.err.println(uploadFileName);
fileName = fileName + uploadFileName.substring(uploadFileName.lastIndexOf("."));
File target = new File(getSavePath(), fileName);
//直接調用org.apache.commons.io.FileUtils下的copyFile 將上傳的附件複製到指定的目錄下
//FileUtils.copyFile(upload, target);
//另外還可以通過輸入流讀取字符之後 用輸出流寫入到磁盤中也是一樣的。
OutputStream outputStream = new FileOutputStream(new File(getSavePath(),fileName));
InputStream inputStream = new FileInputStream(getUpload());
byte[] buffers = new byte[1024];
int length = 0;
while((length = inputStream.read(buffers))!=-1){
outputStream.write(buffers, 0, length);
}
inputStream.close();
outputStream.close();
house.setAttachUrl(savePath + Constant.SEPARATOR + fileName);
houseBiz.saveHouse(house);
ServletActionContext.getRequest().setAttribute("attachurl", house.getAttachUrl());
上面代碼中 有兩種實現將上傳的附件放到指定目錄的方法,都可以實現的。另外第一種更加方便一些,直接調用org.apache.commons.io.FileUtils下的copyFile 將上傳的附件複製到指定的目錄下。
然後struts.xml配置文件中需要配置一下我們action的跳轉,以及附件保存路徑等等信息,具體如下:
<package name="house" namespace="/house" extends="struts-default">
<action name="*" class="com.struts2.web.action.HouseAction" method="{1}">
<result name="listPage">/WEB-INF/system/page/house_list.jsp</result>
<result name="detail">/WEB-INF/system/page/house_detail.jsp</result>
<result name="detail">/WEB-INF/system/page/house_detail.jsp</result>
<result name="loadSuccess">/WEB-INF/system/page/saveHouse.jsp</result>
<param name="savePath">/attach</param>
<interceptor-ref name="defaultStack">
<!-- 配置允許上傳的文件類型,多個用","分隔 -->
<param name="fileUpload.allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/jpg ,image/x-png, image/pjpeg
</param>
<!-- 配置允許上傳的文件大小,單位字節 -->
<param name="fileUpload.maximumSize">102400</param>
</interceptor-ref>
<result name="saveSuccess">/WEB-INF/system/page/saveHouseSuccess.jsp</result>
<result name="input">/WEB-INF/system/page/saveHouseFail.jsp</result>
</action>
</package>
這裏需要指定上傳的附件指定的位置,即加一個<param>標籤就好了,然後指定 上傳的附件的限制類型、以及上傳大小。
然後下面這種配置 是網上很多,反正我試了很多次,都不起作用。
<interceptor-ref name="fileUpload">
<param name="allowedTypes">image/jpg,image/png,image/gif,image/jpeg,image/pjpeg</param>
<param name="maximunSize">10000</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
實踐證明 <interceptor-ref name="defaultStack"></interceptor-ref> 必須加上,否則接收的所有參數都爲null,因爲上面有引用fileUpload這個攔截器。
接下來就可以部署項目,啓動服務器運行了。
首先選擇附件,效果如下:
選擇發佈按鈕,然後服務器就會進行上傳操作。效果如下:
然後 在控制檯可以在到action中輸出的上傳附件信息。效果如下:
可見,在請求提交後服務器先把附件放在服務器的一個臨時文件夾下,當真正上傳成功後就會把該臨時文件刪除。
這樣,使用struts2上傳單個文件效果完成了。
四、多附件上傳
接下來看看多附件上傳,在upload上傳頁面肯定需要多個file控件,代碼如下:
<form name="saveForm" action="${ctx}/house/save.action" method="post" οnsubmit="return checkSaveHouseForm();" enctype="multipart/form-data">
<tr>
<td class="field">上傳附件:</td>
<td>
<input type="file" name="upload" class="text" οnblur="checkUpload()"/>
<span id="sp_upload" class="message"></span>
<br />
</td>
</tr>
<tr>
<td class="field">附件名稱:</td>
<td>
<input type="text" name="fileName" class="text"/>
</td>
</tr>
<tr>
<td class="field">上傳附件:</td>
<td>
<input type="file" name="upload" class="text"/>
<br />
</td>
</tr>
<tr>
<td class="field">附件名稱:</td>
<td>
<input type="text" name="fileName" class="text" οnblur="checkFileName()"/>
<span id="sp_fileName" class="message"></span>
</td>
</tr>
</table>
<div class="buttons"><input type="submit" name="submit" value="發佈"/></div>
</form>
較上面而言 僅僅多了一個附件控件 和 附件名稱控件。
然後後臺需要改了,原先單個附件上傳可以定義FIle 類型的對象接收參數,現在可是多個。所以需要重新定義屬性,代碼如下:
private List<File> upload; //upload file
private List<String> uploadFileName; //附件名稱
private List<String> uploadContentType; //附件類型
private String savePath; //附件保存的路徑
private List<String> fileName ; //用戶輸入的名稱
//省略了get set 方法
實踐 發現,這裏定義 List 類型或者該屬性原有類型的數組 都可以接收成功,可見struts2還是蠻強大的。
比如:
private File[] upload; //upload file
private String[] uploadFileName; //附件名稱
private String[] uploadContentType; //附件類型
private String savePath; //附件保存的路徑
private String[] fileName ; //用戶輸入的名稱
//省略了get set 方法
然後處理上傳的時候需要用循環來依次上傳,具體代碼如下:
//處理上傳的附件
if(upload!=null){
for (int i=0; i<upload.size(); i++) {
fileName.set(i, fileName.get(i) + uploadFileName.get(i).substring(uploadFileName.get(i).lastIndexOf(".")));
File target = new File(getSavePath(), fileName.get(i));
//直接調用org.apache.commons.io.FileUtils下的copyFile 將上傳的附件複製到指定的目錄下
//FileUtils.copyFile(upload, target);
//另外還可以通過輸入流讀取字符之後 用輸出流寫入到磁盤中也是一樣的。
OutputStream outputStream = new FileOutputStream(new File(getSavePath(),fileName.get(i)));
InputStream inputStream = new FileInputStream(getUpload().get(i));
byte[] buffers = new byte[1024];
int length = 0;
while((length = inputStream.read(buffers))!=-1){
outputStream.write(buffers, 0, length);
}
inputStream.close();
outputStream.close();
if(house.getAttachUrl()!=null && !house.getAttachUrl().equals("")){
house.setAttachUrl(house.getAttachUrl() + "," + savePath + Constant.SEPARATOR + fileName.get(i));
}else{
house.setAttachUrl(savePath + Constant.SEPARATOR + fileName.get(i));
}
}
}
houseBiz.saveHouse(house);
ServletActionContext.getRequest().setAttribute("attachurl", house.getAttachUrl());
顯示上傳後的代碼具體如下:
<div class="box">
<div class="msg">
<p>恭喜:添加住房成功!</p>
<p><a href="${ctx}">返回首頁</a></p>
<%
String attachurl = (String)request.getAttribute("attachurl");
if(attachurl!=null){
String []strs = attachurl.split(",");
for(int i=0; i<strs.length; i++){
out.print("<img src='"+request.getContextPath()+strs[i]+"' width='400' height='270'/><br/><br/>");
}
}
%>
</div>
</div>
ok,再重新部署,啓動服務器然後在執行一遍,效果如下圖:
點擊發布按鈕,效果如下:
界面上是成功了,然後在去附件指定目錄查看 是否真正成功。
這樣,多附件上傳就成功完成了。
五、限制上傳的附件類型和大小
前面已經配置的很清楚了,當我們想要過濾一些不好的情況,比如上傳附件的大小上限,以及類型的限制。可以先在前臺判斷,當然如果前臺被屏蔽了 就只能通過後臺再次判斷上傳的附件是否滿足需求,否則服務器會被搞垮的。
當附件的大小超過上限,默認的2M大小,如果給超過上限 就不會傳到服務器,服務器不會拋出異常,進入sturts.xml中配置的input對應的頁面,然後可以在頁面中添加
<s:fielderror></s:fielderror> 標籤捕捉具體異常。
具體異常信息顯示如下圖所示:
當上傳的附件不是圖片格式的話,也不會上傳到服務器,會顯示如下結果:
這樣子顯示好嗎?感覺不太友好,全是一片英文字母。所以爲了友好顯示後臺拋出的異常可以這樣:
在處理上傳附件的Action中重寫addActionError(),當異常類型是超出上限的時候我就修改提示,具體代碼如下:
@Override
public void addActionError(String anErrorMessage) {
if (anErrorMessage.startsWith("the request was rejected because its size")) {
super.addActionError("抱歉,你上傳的文件超過了上限 !!");
} else {
super.addActionError(anErrorMessage);
}
}
說實話,這種方式在我以前的項目中可以實現,但是這裏一直不成功,我也不知道爲什麼。。。。
不過可以用另一種方法,給struts指定一個屬性文件,比如struts.properties ,直接放在classpath的根目錄下。
例子如下:
struts.messages.error.content.type.not.allowed=\u62B1\u6B49\uFF0C\u60A8\u4E0A\u4F20\u7684\u9644\u4EF6\u4E0D\u662F\u56FE\u7247\u683C\u5F0F\u7684\uFF01
struts.messages.error.file.too.large=\u62B1\u6B49\uFF0C\u60A8\u4E0A\u4F20\u7684\u9644\u4EF6\u592A\u5927\uFF0C\u6700\u591A10M!
struts.messages.error.uploading=\u62B1\u6B49\uFF0C\u4E0A\u4F20\u51FA\u9519\u4E86\uFF01
然後在struts的配置文件中指定一下,就好了,具體如下:<!-- 指定struts屬性文件 -->
<constant name="struts.custom.i18n.resources" value="struts" />
然後在試試看就會發現效果以前好多了,如下圖:
這個是附件太大的友好提示,因爲傳了兩個附件,所有顯示了兩個。
這個是附件類型錯誤的友好提示。
六、實現下載功能
其實這個下載功能和在Servlet 中處理的實現方式一樣,所以不在累贅。servlet上傳附件的地址:http://blog.csdn.net/ch656409110/article/details/9502143