1. 程式人生 > >struts2檔案的上傳與下載--超出指定檔案大小的校驗提示

struts2檔案的上傳與下載--超出指定檔案大小的校驗提示

在做B/S架構專案時,經常會遇到檔案上傳與下載的需求,在struts2框架中幫我們實現的檔案上傳與下載機制,能夠很好地實現專案需求。在使用的時候,需要匯入檔案上傳與下載需要的兩個jar,一個是commons-fileupload-x.x.x.jar,另一個是commons-io-x.x.x.jar.

一、檔案上傳的表單(多檔案上傳)

在做檔案上傳時,表單中的method方法必須是post,裡邊的enctype必須是multipart/form-data。不然會出現莫名其妙的異常。多檔案上傳的表單通過js技術來做控制。整個上傳檔案的頁面是:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>

<script type="text/javascript" language="JavaScript"
	src="${pageContext.request.contextPath }/script/calendar.js"
	charset="gb2312"></script>
<script language="javascript">
	function checkf() {
		var files = document.getElementsByName("file");
		if (files[0].value.length != 0) {
			return true;
		} else {
			alert("請選擇檔案");
			return false;
		}
	}
	//新增一行
	function addMore() {
		var td = document.getElementById("more");
		//換行
		var br = document.createElement("br");
		var input = document.createElement("input");
		var button = document.createElement("input");
		input.type = "file";
		input.name = "file";
		button.type = "button";
		button.value = "Remove";
		button.onclick = function() {
			td.removeChild(br);
			td.removeChild(input);
			td.removeChild(button);
		}
		td.appendChild(br);
		td.appendChild(input);
		td.appendChild(button);
	}
</script>

<html>
<head>
<title>測試檔案上傳的jsp</title>
<link href="${pageContext.request.contextPath }/css/Style.css"
	type="text/css" rel="stylesheet">
</head>

<body>

	<s:form action="upLoadAction_upload.do" method="post" theme="simple"
		enctype="multipart/form-data">
		<table align="center" width="50%" border="1">
			<tr>
				<td>選擇上傳檔案</td>
				<td id="more"><s:file name="file"></s:file> <input
					type="button" value="Add More.." onclick="addMore()"></td>
			</tr>
			<tr>
				<td><s:submit type="button" value="submit"
						onclick="return checkf()" /></td>
				<td><s:reset value="reset "></s:reset></td>
			</tr>
		</table>
	</s:form>
</body>
</html>

二、檔案上傳的Action

在檔案上傳的action中必須要有三個引數,分別是File型別的file-上傳的檔案;String型別的fileFileName-上傳檔案的名稱;String型別的fileContentType-檔案型別。如果是多檔案上傳,使用List集合去包裹,也就是List<File> file-上傳檔案集合;List<Sting> fileFileName-檔名稱集合;List<String> fileContentType-檔案型別集合。

package cn.ysu.zhy.devmanager.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Controller;

@Controller("upLoadAction")
public class UpLoadAction extends BaseAction {
	/**
	 * 1.做檔案上傳必須得三個引數:file,fileFileName,fileContentType
	 */
	/**
	 * 對應jsp的file標籤,表示上傳的檔案(多檔案用集合表示)
	 */
	private List<File> file;
	/**
	 * 檔名稱
	 */
	private List<String> fileFileName;

	/**
	 * 檔案的型別
	 */
	private List<String> fileContentType;
	/**
	 * 2.下載的檔名
	 */
	private String fileName;
	private String fileUploadErrorMessage;

	public List<File> getFile() {
		return file;
	}

	public void setFile(List<File> file) {
		this.file = file;
	}

	public List<String> getFileFileName() {
		return fileFileName;
	}

	public void setFileFileName(List<String> fileFileName) {
		this.fileFileName = fileFileName;
	}

	public List<String> getFileContentType() {
		return fileContentType;
	}

	public void setFileContentType(List<String> fileContentType) {
		this.fileContentType = fileContentType;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	/**
	 * @Name: home
	 * @Description: 跳轉到上傳首頁upload.jsp
	 * @Author: ZHY(作者)
	 * @Version: V1.00 (版本號)
	 * @Create Date: 2016-10-07 (建立日期)
	 * @Parameters: 無
	 * @Return: String 跳轉到上傳首頁upload.jsp
	 */
	public String home() {

		return "home";
	}

	/**
	 * @throws Exception
	 * @Name: upload
	 * @Description: 處理檔案的上傳
	 * @Author: ZHY(作者)
	 * @Version: V1.00 (版本號)
	 * @Create Date: 2016-10-07 (建立日期)
	 * @Parameters: 無
	 * @Return: String 上傳成功,重定向到upload.jsp
	 */
	public String upload() throws Exception {
		// 1.獲取上傳的檔案路徑
		File filePath = new File(ServletActionContext.getServletContext().getRealPath("myUploadFile"));
		// 2.判斷目錄是否存在,如果不存在建立資料夾
		if (!filePath.exists()) {
			filePath.mkdir();// 建立目錄
		}
		System.out.println(filePath);

		if (file != null && file.size() > 0 && fileFileName != null && fileFileName.size() > 0) {
			for (int i = 0; i < file.size(); i++) {
				// 3.宣告檔案的輸入流,為輸入流指定路徑
				FileInputStream is = new FileInputStream(file.get(i));
				// 獲取上傳檔案的大小(單位是位元組1257114280--是1.17G),做資料狹小的限制,如果大於209715200,也就是200M,return
				// error,提示上傳檔案大小不能超過200M,2097152--2M
				int length = is.available();// 獲取上傳檔案的位元組大小
				// 上傳的檔案超過200M
				if (length >= 209715200) {

					request.setAttribute("error", "您上傳的檔案超過200M");

					return "error";
				}

				// 4.獲取檔案輸出流,宣告輸出流的地址已及名稱
				FileOutputStream out = new FileOutputStream(filePath + "\\" + fileFileName.get(i));
				try {
					// 5.流對接
					byte[] buffer = new byte[1024];
					int len = 0;
					while ((len = is.read(buffer)) > 0) {
						// 將buffer裡的二進位制數,從0開始,每次寫len這麼長,寫入輸出流中
						out.write(buffer, 0, len);
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					is.close();
					out.close();
				}

			}
		}

		request.setAttribute("fileName", fileFileName);

		return "upload";
	}

	/**
	 * @Name: upload
	 * @Description: 處理檔案的下載
	 * @Author: ZHY(作者)
	 * @Version: V1.00 (版本號)
	 * @Create Date: 2016-10-07 (建立日期)
	 * @Parameters: 無
	 * @Return: String 下載成功,重定向到upload.jsp
	 */
	public String download() {

		return "download";
	}

}

三、檔案下載的表單

檔案下載相對於檔案上傳要簡單一些,在JSP中唯一需要注意的就是中文亂碼問題,解決亂碼問題使用URLEncoder和URLDecoder基本上就能解決了。

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>

<script type="text/javascript" language="JavaScript"
	src="${pageContext.request.contextPath }/script/calendar.js"
	charset="gb2312"></script>

<html>
<head>
<title>測試檔案下載的jsp</title>
<link href="${pageContext.request.contextPath }/css/Style.css"
	type="text/css" rel="stylesheet">
</head>

<body>

	<table align="center" width="50%" border="1">
		<s:if test="#request.fileName != null && #request.fileName.size() > 0">
			<s:iterator value="#request.fileName" var="name">
				<s:set value="name" id="names" name="names" scope="Request" />
				<%
					//沒有進行編碼
							String fname = (String) request.getAttribute("names");
							//編碼之後的值
							String fnameEncode = java.net.URLEncoder.encode(fname, "UTF-8");
				%>
				<tr>
					<td><%=fname%></td>
					<td><a
						href="<s:url value='downLoadAction_download.do'><s:param name='fileName'><%=fnameEncode%></s:param> </s:url>">下載</a>
					</td>
				</tr>
			</s:iterator>
		</s:if>
	</table>
</body>
</html>

四、檔案下載的Action

在檔案下載的action中,必須有兩個引數,一個是String型別的fileName-檔名稱和InputStream型別的fileInput-檔案下載的流。

package cn.ysu.zhy.devmanager.action;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import org.apache.struts2.ServletActionContext;
import org.springframework.stereotype.Controller;

/**
 * 檔案下載的action
 * 
 * @author ZHY
 * 
 */
@Controller("downLoadAction")
public class DownLoadAction extends BaseAction {
	/**
	 * 下載檔案的名稱
	 */
	private String fileName;
	/**
	 * 下載的檔案
	 */
	private InputStream fileInput;

	public String getFileName() {

		return fileName;
	}

	public void setFileName(String fileName) {
		try {
			fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		this.fileName = fileName;
	}

	public InputStream getFileInput() {
		try {
			fileName = URLDecoder.decode(fileName, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return ServletActionContext.getServletContext().getResourceAsStream("myUploadFile\\" + fileName);
	}

	public void setFileInput(InputStream fileInput) {
		this.fileInput = fileInput;
	}

	/**
	 * 下載時,顯示中文名字
	 * 
	 */
	public String getDownloadFileName() {
		String downloadFileName = fileName;
		try {
			// 使用ISO8859-1編碼
			downloadFileName = new String(downloadFileName.getBytes(), "ISO8859-1");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return downloadFileName;
	}

	/**
	 * @Name: download
	 * @Description: 處理檔案的上傳
	 * @Author: ZHY(作者)
	 * @Version: V1.00 (版本號)
	 * @Create Date: 2016-10-07 (建立日期)
	 * @Parameters: 無
	 * @Return: String 上傳成功,重定向到upload.jsp
	 */
	public String download() {
		try {
			fileName = URLDecoder.decode(fileName, "UTF-8");
		} catch (Exception e) {
			e.printStackTrace();
		}
		fileInput = ServletActionContext.getServletContext().getResourceAsStream("myUploadFile\\" + fileName);
		if (fileInput == null) {
			return "error";
		}
		return "download";
	}

}
OK了,至此,檔案上傳與下載的功能就實現了。但是存在一個問題,就是struts2中預設支援能夠上傳的檔案大小是2M,在專案開發過程中,一般上傳的檔案都要大於2M,這個問題也好解決,只需要在struts.xml中,配置一個constant常量,將檔案的最大值設定成想要的。
<!-- 允許檔案上傳的大小,預設為2M 2097152 在這裡設定成自己想要的-->
<constant name="struts.multipart.maxSize" value="2097152"></constant>

五、超出指定上傳檔案大小之後的提示

在專案中,往往有這種需求:當用戶上傳的檔案大於系統設定的值的時候,需要提示使用者,如果直接報錯,使用者體驗非常不好。我在做這部分需求的時候,走了不少彎路,也查閱了大量的網上的資料,但是最終還是遇到了一個跨不過去的坑(對於我來說的,可能是自己知識儲備不到位)。

我查閱了很多資料,都是說需要重寫action中的addActionError方法,將上傳檔案的錯誤轉變成action級別的錯誤,我這樣做了,也在addActionError中獲取到了相應的檔案大小,將錯誤資訊新增到supper.addActionError中之後,無論我如何,在jsp中也獲取不到我自己丟擲的錯誤資訊,而是頁面直接報錯了。這個問題困擾了我很久,也跟一些人討論過,但是最終還是沒有一個完美的解釋。於是,我想了一個比較笨的方法:

1、首先在constant中配置一個很大的最大值,這個值可以設定的大一點,我這裡設定成了2000M

PS:我主要是最文件檔案上傳,也就是一些doc、pdf等,一幫不可能超過這麼大。我係統中的需求是不能大於200M。

2、在檔案上傳的action中,獲取上傳上來的檔案的大小,跟200M做一個比較,如果大於200M,那麼將錯誤資訊寫到request作用域中,然後return error。否則正常執行檔案上傳的操作。對應程式碼中的位置:

// 宣告檔案的輸入流,為輸入流指定路徑
FileInputStream is = new FileInputStream(file.get(i));
// 獲取上傳檔案的大小(單位是位元組1257114280--是1.17G),做資料狹小的限制,如果大於209715200,也就是200M,return
// error,提示上傳檔案大小不能超過200M,2097152--2M
int length = is.available();// 獲取上傳檔案的位元組大小
// 上傳的檔案超過200M
if (length >= 209715200) {
	
        request.setAttribute("error", "您上傳的檔案超過200M");
	return "error";
}
在錯誤頁面中,使用struts的標籤,得到自己丟擲的錯誤資訊,提示使用者。
<s:property value="#request.error" />
PS:我這個測試demo是在SSH框架的基礎上進行開發的,所以原始碼中會涉及到一些hibernate和spring相關的知識。