1. 程式人生 > >Struts2實現單檔案上傳,多檔案上傳與下載(十)

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()時設定一下,最後取出來的時候再轉換一下。

謝謝!!!