Struts2實現單檔案上傳,多檔案上傳與下載(十)
“兩個蝴蝶飛”特別喜歡"java1234知識分享網"小峰的實用主義,所以本文及其系列文章均是採用實用主義,從專案和程式碼的角度去分析。由於本人經驗有限,嘴皮子不溜,所以學術性,概念性,底層性的知識點暫時不做介紹。文章中有錯誤之處,歡迎拍磚和指點。特別感謝"java1234知識分享網 "和"黑馬程式設計師官網",所有的資料大部分是兩者提供,為了方便書寫,故不一一指名出處,請諒解,非常抱歉。
上一章簡單介紹了Struts2實現國際化操作及中英文切換(九)如果沒有看過,請觀看上一章
上傳檔案和下載檔案的重要性就不說了,常用於上傳文件和頭像等。 Struts2中有一個檔案上傳的攔截器。
Struts2上傳時,依賴apache的兩個jar包
先建立一個基本的Struts2執行環境(這裡不做介紹了)
一 實現單檔案上傳
一.一 在檔案上傳時,需要注意:
1. Struts2上傳檔案時,用<s:file name="upload"> 或者<input type="file" name="upload" />均可。(但最好與jquery外掛聯合使用,達到上傳前預覽的效果)。
2. form表單提交時,提交方式為post提交。(避免檔案過大,get方式不支援)
3.form表單提交時,enctype型別需要改變,改變成:
enctype="multipart/form-data"
4.相應的Action進行接收時,File Xxx需要與<s:file >中的name相同,如<s:file> name為upload,則Action中File相應的為upload. <s:file>name為uploadImage,則Action中File為uploadeImage. 需要保持一致。
5.Action中取得的File upload.getName() 取得的並不是上傳檔案的真實名稱,只是一個快取的名稱,沒有任何的意義。
6.Action中想取得上傳檔案的名稱和上傳型別,需要定義兩個String 型別,格式必須是XxxFileName,XxxContentType。其中Xxx為File的值。
一.二 編寫 上傳表單頁面
<s:form action="File_single.action" namespace="/" method="post" enctype="multipart/form-data"> 使用者名稱: <s:textfield name="name"/> <br/> 密碼: <s:password name="password"/><br/> 上傳檔案:<s:file name="upload"/><br/> <s:submit value="提交"/> <s:reset value="重置"/> </s:form>
注意,名稱為upload。
一.三 編寫相應的Action,FileAction
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import com.yjl.pojo.User;
/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 28, 2018 8:35:56 AM
* 關於檔案上傳和下載的Action實現
*/
public class FileAction extends ActionSupport implements ModelDriven<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
//在檔案上傳時,也同樣可以獲得其他標籤的值.
private User user=new User();
@Override
public User getModel() {
return user;
}
/**
* 關於檔案上傳的具體的操作
* @param uploade 與前端表單中file標籤的name值相同.
* @param XxxFileName 上傳檔案的名稱
* @param XxxContentType 上傳檔案的型別
*/
private File upload;
private String uploadFileName;
private String uploadContentType;
//實現它們三個的setter和getter方法
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;
}
/**
*單檔案上傳操作
*/
public String single(){
//在實際開發中,需要判斷一下是否上傳了檔案,即upload是否為空,不為空,才進行上傳的操作。
logger.info("可以取出user的值:"+user.toString());
if(upload!=null){
//1.輸出上傳的型別
logger.info("上傳檔案的名稱用File取得:"+this.getUpload().getName());
logger.info("直接用屬性取得名稱:"+this.getUploadFileName());
logger.info("檔案上傳的型別:"+this.getUploadContentType());
//2.設定上傳檔案的放置位置,通常放在伺服器下面的upload資料夾下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.這是一個目錄,如果這個目錄不存在,需要建立這個目錄(包括其父目錄)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常還需要對上傳的檔名進行UUID的操作,使檔名不能重複。這裡不做處理
String fileName=this.getUploadFileName();
//5.呼叫FileUtils類執行上傳的操作.
try {
FileUtils.copyFile(upload,new File(srcFile,fileName));
logger.info("檔案上傳成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return SUCCESS;
}
}
一.四 配置struts.xml檔案
<package name="file" extends="struts-default" namespace="/">
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/success.jsp</result>
</action>
</package>
一.五 重啟伺服器,驗證檔案上傳
檢視logger輸出
file.getName()並沒有任何意義。
檢視伺服器upload資料夾下是否有這張圖片:
存在圖片,檔案上傳操作成功。
一. 六 檔案上傳操作時的攔截器配置
1.在上傳的時候,一般需要特別指定檔案上傳時的格式和大小。 Struts2自帶的檔案上傳攔截器已經幫我們實現了這一點,我們只需要進行簡單的配置即可。
在struts.xml配置檔案中:
<package name="file" extends="struts-default" namespace="/">
<interceptors>
<interceptor-stack name="defaultStack">
<!-- 引入已經定義好的檔案上傳攔截器,將引數傳入進去,名稱為fileUpload -->
<interceptor-ref name="fileUpload">
<!-- 上傳檔案的大小 -->
<param name="maximumSize">500000000</param>
<!-- 標準MINE名稱 -->
<param name="allowedTypes">text/plain,application/vnd.ms-powerpoint</param>
<!-- 字尾名 可以傳遞.txt和.ppt結尾的,此時圖片是不行的-->
<param name="allowedExtensions">.txt,.ppt</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/success.jsp</result>
</action>
</package>
如果此刻上傳圖片的話:
會顯示異常:
上傳.txt文件的話:
是可以正常上傳的。
一般設定圖片時,常用的格式為:
<interceptors>
<interceptor-stack name="defaultStack">
<!-- 引入已經定義好的檔案上傳攔截器,將引數傳入進去,名稱為fileUpload -->
<interceptor-ref name="fileUpload">
<!-- 上傳檔案的大小 -->
<param name="maximumSize">500000000</param>
<!-- 字尾名 設定常見的圖片形式-->
<param name="allowedExtensions">.bmp,.jpg,.png,.gif</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</interceptor-stack>
</interceptors>
2. 有的時候,可能會上傳視訊或者其他大檔案,可以在struts.xml常量中配置相應的上傳檔案大小。
預設的大小是2M,即使你在設定型別時,用了maximumSize,設定了一個很大的值。 那麼當大小超過2M時,也是錯誤的。 這個時候需要設定另外一個引數。常量:struts.multipart.maxSize
<constant name="struts.multipart.maxSize" value="1000000000"/>
二 實現多檔案上傳
在實現多檔案上傳時,在單檔案上傳的基礎上,是非常簡單的。只需要改變成陣列樣式即可。將File upload改成File []upload即可。 uploadFileName和uploadeContentType也同樣需要改變成字串陣列的樣式。
Action中:
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 28, 2018 8:35:56 AM
* 關於檔案上傳和下載的Action實現
*/
public class FileAction extends ActionSupport{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
/**
* 關於檔案上傳的具體的操作
* @param uploade 與前端表單中file標籤的name值相同.
* @param XxxFileName 上傳檔案的名稱
* @param XxxContentType 上傳檔案的型別
*/
private File []upload;
private String []uploadFileName;
private String []uploadContentType;
//實現它們三個的setter和getter方法
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;
}
/**
*多檔案上傳操作
*/
public String multiple(){
//在實際開發中,需要判斷一下是否上傳了檔案,即upload是否為空,不為空,才進行上傳的操作。
if(upload!=null){
for(int i=0;i<upload.length;i++){
//1.輸出上傳的型別
logger.info("上傳檔案的名稱用File取得:"+this.getUpload()[i].getName());
logger.info("直接用屬性取得名稱:"+this.getUploadFileName()[i]);
logger.info("檔案上傳的型別:"+this.getUploadContentType()[i]);
//2.設定上傳檔案的放置位置,通常放在伺服器下面的upload資料夾下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.這是一個目錄,如果這個目錄不存在,需要建立這個目錄(包括其父目錄)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常還需要對上傳的檔名進行UUID的操作,使檔名不能重複。這裡不做處理
String fileName=this.getUploadFileName()[i];
//5.呼叫FileUtils類執行上傳的操作.
try {
FileUtils.copyFile(upload[i],new File(srcFile,fileName));
logger.info("檔案上傳成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
return SUCCESS;
}
}
struts.xml
<package name="file" extends="struts-default" namespace="/">
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/success.jsp</result>
</action>
</package>
login.jsp頁面
file是多選形式,用多個name值相同的樣式。
<s:form action="File_multiple.action" namespace="/" method="post" enctype="multipart/form-data">
使用者名稱: <s:textfield name="name"/> <br/>
密碼: <s:password name="password"/><br/>
<!-- 名稱相同 -->
上傳檔案1:<s:file name="upload"/><br/>
上傳檔案2:<s:file name="upload"/><br/>
<s:submit value="提交"/>
<s:reset value="重置"/>
</s:form>
重啟伺服器,進行相關的測試:
多檔案上傳操作成功。
三 檔案上傳例項應用
在實際專案中,不僅要能正常的上傳檔案,還應該把上傳檔案的位置返回。 前端頁面和struts.xml的配置都一樣,主要是對Action的處理。 我通常的作法是建立一個BaseAction的類,與第四章專案中的BaseAction相同,只是這裡添加了上傳的操作。
BaseAction
package com.yjl.web.action;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
/**
* @author 兩個蝴蝶飛
* @version 建立時間:2018年8月23日 下午4:40:13
* @description BaseAction的工具類
*/
@SuppressWarnings(value= {"rawtypes","unchecked"})
public class BaseAction<T> extends ActionSupport implements ModelDriven<T>{
private static final long serialVersionUID = -7180401147510521582L;
private Logger logger=Logger.getLogger(BaseAction.class);
private T t;
private Class clazz;
public BaseAction() {
//得到當前的類
Class class1=this.getClass();
//得到執行中的父類
ParameterizedType parameterizedType=(ParameterizedType) class1.getGenericSuperclass();
clazz=(Class) parameterizedType.getActualTypeArguments()[0];
try {
t=(T) clazz.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("當前類的類:"+clazz.getName()+"完成初始化");
}
@Override
public T getModel() {
return t;
}
//新增實現檔案上傳和下載的操作
/**
* 關於檔案上傳的具體的操作
* @param uploade 與前端表單中file標籤的name值相同.
* @param XxxFileName 上傳檔案的名稱
* @param XxxContentType 上傳檔案的型別
*/
private File upload;
private String uploadFileName;
private String uploadContentType;
//實現它們三個的setter和getter方法
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;
}
/**
*得到上傳的路徑
*/
public String getUploadPath(){
//在實際開發中,需要判斷一下是否上傳了檔案,即upload是否為空,不為空,才進行上傳的操作。
String contextPath=null;
if(upload!=null){
//1.輸出上傳的型別
logger.info("上傳檔案的名稱用File取得:"+this.getUpload().getName());
logger.info("直接用屬性取得名稱:"+this.getUploadFileName());
logger.info("檔案上傳的型別:"+this.getUploadContentType());
//2.設定上傳檔案的放置位置,通常放在伺服器下面的upload資料夾下.
String path=ServletActionContext.getServletContext().getRealPath("/upload");
//3.這是一個目錄,如果這個目錄不存在,需要建立這個目錄(包括其父目錄)
File srcFile=new File(path);
if(!srcFile.exists()){
srcFile.mkdirs();
}
//4.通常還需要對上傳的檔名進行UUID的操作,使檔名不能重複。檔案的返回路徑是fileName.
String fileName=path+getAnglePath(this.getUploadFileName());
//5.呼叫FileUtils類執行上傳的操作.
try {
//寫複製方法
FileUtils.copyFile(upload,new File(fileName));
//將fileName進行相應的處理,去掉前面的一些無用的東西。 得到專案名
String context=ServletActionContext.getRequest().getContextPath().substring(1);
contextPath=getPath(fileName,context);
logger.info("在Tomcat下的目錄為:"+contextPath);
logger.info("檔案上傳成功");
} catch (IOException e) {
e.printStackTrace();
}
}
return contextPath;
}
/**
* 將一個檔名設定成唯一的檔名
* @param fileName 普通的檔名
* @return 返回一個唯一的檔名
*/
public String getAnglePath(String fileName) {
//不採用UUID的形式,可以自定義當前的時間字串來處理.
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmssSSS");
String angle=sdf.format(new java.util.Date());
return File.separator+angle+"_"+fileName;
}
/**
* 將磁碟上的檔案在Tomcat下的檔案,轉換成在Tomcat上的檔案
* @param realPath 在磁碟上的路徑
* @param name 專案名
* @return 將磁碟上的檔案在Tomcat下的檔案,轉換成在Tomcat上的檔案
*/
public String getPath(String realPath, String name) {
int index = realPath.indexOf(name);
String[] args = realPath.substring(index).split("\\\\");
StringBuffer sb = new StringBuffer("/");
for (int i = 0; i < args.length; i++) {
if (i != args.length - 1) {
sb.append(args[i]).append("/");
} else {
sb.append(args[i]);
}
}
return sb.toString();
}
}
在我們寫的FileAction中,只需要繼承BaseAction類,然後呼叫方法即可。
package com.yjl.web.action;
import org.apache.log4j.Logger;
import com.yjl.pojo.User;
/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 28, 2018 8:35:56 AM
* 關於檔案上傳和下載的Action實現
*/
public class FileAction extends BaseAction<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
public String login(){
String path=getUploadPath();
logger.info("上傳的路徑是:"+path);
return SUCCESS;
}
}
檔案上傳後的控制檯:
如果沒有上傳檔案的話,
這樣就完成了。
有的在上傳時,會出現中文亂碼的問題,可以在struts.xml中新增格式處理常量:
<!--修改國際化編碼 -->
<constant name="struts.i18n.encoding" value="UTF-8"></constant>
檔案上傳操作完成。
四 檔案下載
在下載時,只需要指定檔案的名稱,就會去相應的下載資料夾中去尋找,去下載。
在該專案下新建一個upload資料夾,裡面放置一些檔案。
四.一 下載前查詢所有的檔案
一般在下載之前,會將可以下載的檔案以列表的形式進行顯示,或者將那個檔案進行相應的顯示。
FileAction中:
package com.yjl.web.action;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.struts2.ServletActionContext;
import com.yjl.pojo.User;
/**
* @author 兩個蝴蝶飛
* @version 建立時間:Aug 28, 2018 8:35:56 AM
* 關於檔案上傳和下載的Action實現
*/
public class FileAction extends BaseAction<User>{
private static final long serialVersionUID = 1L;
private Logger logger=Logger.getLogger(FileAction.class);
public String login(){
String path=getUploadPath();
logger.info("上傳的路徑是:"+path);
return SUCCESS;
}
//要下載的檔案
private List<File> downFileList=new ArrayList<File>();
public List<File> getDownFileList() {
return downFileList;
}
public void setDownFileList(List<File> downFileList) {
this.downFileList = downFileList;
}
public String downFileList(){
//1.找到path路徑
String path=ServletActionContext.getServletContext().getRealPath("/upload");
File pFile=new File(path);
//2.找到pFile資料夾下及下屬資料夾下所有的檔案。
List<File> temp=new ArrayList<File>();
//3.將所有的檔案都放置到列表中
downFileList=ergodic(pFile, temp);
logger.info("長度是:"+downFileList.size());
//4. 將集合轉換成陣列
return "downFileList";
}
private List<File> ergodic(File file,List<File> resultFileList){
if(file==null||!file.exists()){
return new ArrayList<File>();
}
File[] files = file.listFiles();
if(files==null||files.length<=0){
return new ArrayList<File>();
}
for (File f : files) {
if(f.isDirectory()){// 判斷是否資料夾
ergodic(f,resultFileList);// 呼叫自身,查詢子目錄
}else{
resultFileList.add(f);
}
}
return resultFileList;
}
}
在Action中看有的人是用的File []陣列,這裡用集合了,也是可以的。
在struts.xml中是正常的配置。
<package name="file" extends="struts-default" namespace="/">
<action name="File_*" class="com.yjl.web.action.FileAction" method="{1}">
<result name="success">/success.jsp</result>
<result name="downFileList">/list.jsp</result>
</action>
</package>
在前端頁面處:
<body>
這是可以下載檔案的列表<br/>
<s:iterator value="downFileList" var="f">
<s:property value="#f.name"/><s:a action="getDownFile?fileName=%{#f.name}" namespace="/">下載</s:a>
<br/>
</s:iterator>
</body>
四.二 將檔名稱傳遞進去,下載相應的檔案
主要的Action程式碼:
//下載檔案時所用的操作
//@param fileName 下載的檔名
private String fileName;
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
//需要進行get方式提交時中文亂碼的情況
try {
this.fileName=new String(fileName.getBytes("ISO-8859-1"),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public InputStream getDownloadFile() throws Exception{
String filePath = ServletActionContext.getRequest()
.getServletContext().getRealPath("/upload");
String realPath=filePath+File.separator+fileName;
InputStream is = new FileInputStream(new File(realPath));
//處理中文亂碼
fileName = URLEncoder.encode(fileName, "UTF-8");
//在最後顯示時,需要將前面的那些日期去掉。
fileName=fileName.split("_")[1];
return is;
}
2. 配置struts.xml, 注意,這個時候並沒有相應的method的配置。
<action name="getDownloadFile" class="com.yjl.web.action.FileAction">
<!--下載時候的配置-->
<result type="stream">
<!-- 下載的檔案資料存放的方法,該方法返回一個InputStream
例如取值為inputStream的屬性需要編寫getInputStream()方法-->
<param name="inputName">downloadFile</param>
<!--下載時,客戶端顯示的下載的檔名 -->
<param name="contentDisposition">attachment;filename=${fileName}</param>
<!-- 資料的緩衝大小 -->
<param name="bufferSize">1024</param>
</result>
</action>
單獨另起了一個action的配置。
重啟伺服器,可以正常的完成下載的操作,並且解決了中文亂碼和中文丟失的問題。 解決方案就是setFileName()時設定一下,最後取出來的時候再轉換一下。
謝謝!!!