步驟如下:
1.重寫、自定義JakartaMultiPartRequest類
package com.hikvision.fileUploadProcess.interceptor;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
public class MyJakartaMultiPartRequest extends JakartaMultiPartRequest {
@Override
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
//什麼也不做
}
}
原因:
struts2默認的攔截器中有一個FileUploadInterceptor,它會攔截所有的MultipartRequest,並且將得到的File及相關信息傳遞給action,因此在action被調用之前,文件上傳已經被處理完了,不能引入監聽文件寫入時文件進度;
struts2處理文件上傳使用的是commons-fileupload,因此我們可以使用ProgressListener。注意我們需要在解析請求的時候加入我們的監聽器,我們首先想到的是替換掉FileUploadInterceptor,不幸的是 FileUploadInterceptor並不執行解析的任務,實際在FileUploadInterceptor被調用之前,MultipartRequest已經被解析了,文件上傳的工作已經完成。而實際上對於所有的文件上傳請求,struts2會爲其生成一個MultiPartRequestWrapper進行包裝,而它維護着一個 MultiPartRequest接口的實例。MultiPartRequest的實現類只有一個 JakartaMultiPartRequest,JakartaMultiPartRequest有一個方法parseRequest,此方法負責解析 request並生成FileItem,即對文件進行讀寫操作,因此我們可以重寫此方法,添加ProgressListener。不幸的是,JakartaMultiPartRequest的很多方法都是private的,我們不能繼承它然後重寫parseRequest方法,JakartaMultiPartRequest實現了MultiPartRequest接口,我們可以編寫一個類,實現 MultiPartRequest接口,替代JakartaMultiPartRequest類的代碼全都拷貝過來,並修改parseRequest方法,完成文件的寫入與進度的監聽。
2.配置struts2.xml
<!-- 1配置自定義文件類myRequestParser,繼承MultiPartRequest重寫 -->
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"
name="myRequestParser" class="com.hikvision.fileUploadProcess.interceptor.MyJakartaMultiPartRequest"
scope="default" optional="true" />
<!-- 注意struts2.3.15.1以前版本這裏爲struts.multipart.handler, struts2.3.15.1(包含2.3.15.1)這裏爲struts.multipart.parser-->
<constant name="struts.multipart.parser" value="myRequestParser" />
<!-- 配置項目所上傳文件的最大的Size爲1000M -->
<constant name="struts.multipart.maxSize" value="1048576000"/>
3.定義文件上傳進度信息的類
package com.hikvision.fileUploadProcess.entity;
/**
* 上傳文件進度信息
*
* @author wanglei
* @version 0.1
*/
public class FileUploadProgress {
// 文件總長度(設置至少爲1字節防止前臺出現/0的情況)
private long length = 1;
// 已上傳的文件長度
private long currentLength = 0;
// 上傳是否完成
private boolean isComplete = false;
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public long getCurrentLength() {
return currentLength;
}
public void setCurrentLength(long currentLength) {
this.currentLength = currentLength;
}
public boolean isComplete() {
return isComplete;
}
public void setComplete(boolean isComplete) {
this.isComplete = isComplete;
}
public FileUploadProgress() {
super();
}
}
4.實現ProgressListener接口
package com.hikvision.fileUploadProcess.impl;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.fileupload.ProgressListener;
import com.hikvision.fileUploadProcess.entity.FileUploadProgress;
/**
* 文件上傳進度消息
* @author hongchenjin
*
*/
public class FileUploadListener implements ProgressListener {
private HttpSession session;
public FileUploadListener(HttpServletRequest request) {
session = request.getSession();
FileUploadProgress fileUploadProgress = new FileUploadProgress();
fileUploadProgress.setComplete(false);
session.setAttribute("fileUploadProgress", fileUploadProgress);
}
//更新進度情況
@Override
public void update(long readedBytes, long totalBytes, int currentItem) {
//實現文件上傳的核心方法
Object attribute = session.getAttribute("fileUploadProgress");
FileUploadProgress fileUploadProgress = null;
if(null == attribute){
fileUploadProgress = new FileUploadProgress();
fileUploadProgress.setComplete(false);
session.setAttribute("fileUploadProgress", fileUploadProgress);
}else{
fileUploadProgress = (FileUploadProgress)attribute;
}
fileUploadProgress.setCurrentLength(readedBytes);
fileUploadProgress.setLength(totalBytes);
if(readedBytes==totalBytes){
fileUploadProgress.setComplete(true);
}else{
fileUploadProgress.setComplete(false);
}
session.setAttribute("progress", fileUploadProgress);
}
}
5.文件上傳進度的action
package com.hikvision.modules.guide.action;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.ServletActionContext;
import com.hikvision.fileUploadProcess.entity.FileUploadProgress;
import com.hikvision.frame.entity.OperateResult;
import com.hikvision.modules.guide.entity.NameToLocalFile;
import com.hikvision.modules.guide.service.GuideService;
import com.hikvision.modules.guide.util.GetSharePathXml;
import com.hikvision.util.AjaxUtil;
import com.opensymphony.xwork2.ActionSupport;
public class GuideUploadAction extends ActionSupport {
private GuideService guideService;
public GuideService getGuideService() {
return guideService;
}
public void setGuideService(GuideService guideService) {
this.guideService = guideService;
}
//文件格式不支持
public void typeNotSupport(){
OperateResult result = new OperateResult(false,"");
result.setResult(false);
result.setMsg("上傳文件最大不能超過100M;支持的格式爲exe,png,jpg,gif,bmp,doc,docx,xls,rar,txt,zip,js,css,msi,pptx");
AjaxUtil.ajaxWrite(result);
}
/**
* 上傳文件
*
* @return
*/
public void uploadfile() {
OperateResult result = new OperateResult(false,"");
try {
HttpServletRequest request = ServletActionContext.getRequest();
//獲取文件備註
String comments = request.getParameter("comments");
//文件上傳
UploadFile.upload(request, ServletActionContext.getResponse());
//將文件名和文件的對應關係存進數據庫裏
NameToLocalFile nameToLocalFile = new NameToLocalFile();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time = sdf.format(new Date());
nameToLocalFile.setCreatetime(time);
//保存在服務器上的路徑
nameToLocalFile.setLocalpath("/" + request.getParameter("variety") + "/" + request.getParameter("name"));
nameToLocalFile.setName(request.getParameter("name"));
nameToLocalFile.setVariety(request.getParameter("variety"));
nameToLocalFile.setComments(comments);
guideService.saveOrUpdate(nameToLocalFile);
result.setResult(true);
result.setMsg("上傳成功");
} catch (IOException e) {
LOG.error("上傳文件發生異常,錯誤原因 : " + e.getMessage());
result.setMsg("上傳文件最大不能超過100M;");
result.setResult(false);
}
AjaxUtil.ajaxWrite(result);
}
/**
* 修改文件
*/
public void updateNameToLocalFile(){
OperateResult result = new OperateResult(false,"");
HttpServletRequest request = ServletActionContext.getRequest();
//種類
String variety = request.getParameter("variety");
//名稱
String name = request.getParameter("name");
//文件備註
String comments = request.getParameter("comments");
//是否上傳了文件
Boolean flag = Boolean.parseBoolean(request.getParameter("flag"));
//id
int id = Integer.parseInt(request.getParameterValues("ids")[0]);
//根據id獲取數據庫的nameToLocalFileById
NameToLocalFile nameToLocalFileById = guideService.getNameToLocalFileById(id);
//分爲兩種情況,第一種爲用戶重新上傳了文件,第二種是用戶只修改了名字和種類
if(flag){
/**
* 用戶重新上傳文件的情況
*/
//刪除原來文件
String localpath = nameToLocalFileById.getLocalpath();
String realPath = GetSharePathXml.getShareFolderPath() + localpath;
File file = new File(realPath);
//用戶上傳了文件
try {
if(file.exists()){
//刪除原來文件(判斷是否存在該文件,存在就刪除)
file.delete();
}
//文件上傳
UploadFile.upload(request, ServletActionContext.getResponse());
result.setResult(true);
result.setMsg("修改成功");
} catch (Exception e) {
result.setResult(false);
result.setMsg("修改失敗");
}
}
//將文件名和文件的對應關係存進數據庫裏
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time = sdf.format(new Date());
nameToLocalFileById.setCreatetime(time);
if(flag){
//保存在服務器上的路徑
nameToLocalFileById.setLocalpath("/" + variety + "/" + name);
nameToLocalFileById.setName(name);
}
nameToLocalFileById.setVariety(variety);
nameToLocalFileById.setComments(comments);
guideService.saveOrUpdate(nameToLocalFileById);
result.setResult(true);
result.setMsg("修改成功");
AjaxUtil.ajaxWrite(result);
}
/**
* 顯示上傳文件進度進度
*
* @return page view
*/
public void progress() {
// 新建當前上傳文件的進度信息對象
FileUploadProgress p = null;
Object attribute = ServletActionContext.getRequest().getSession().getAttribute("fileUploadProgress");
if(null == attribute){
p = new FileUploadProgress();
// 緩存progress對象
ServletActionContext.getRequest().getSession().setAttribute("fileUploadProgress", p);
}else{
p = (FileUploadProgress)attribute;
}
ServletActionContext.getResponse().setContentType("text/html;charset=UTF-8");
ServletActionContext.getResponse().setHeader("pragma", "no-cache");
ServletActionContext.getResponse().setHeader("cache-control", "no-cache");
ServletActionContext.getResponse().setHeader("expires", "0");
//以下方法爲輸出json(封裝,可根據實際情況修改輸出的方式)
AjaxUtil.ajaxWriteObject(p);
}
/**
* 清除session
*/
public void clearProgressSession(){
ServletActionContext.getRequest().getSession().setAttribute("fileUploadProgress", null);
}
}
6.UploadFile類(文件上傳)
package com.hikvision.modules.guide.action;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;
import com.hikvision.fileUploadProcess.impl.FileUploadListener;
import com.hikvision.modules.guide.util.GetSharePathXml;
/**
* upload file
*
* @author scott.Cgi
*/
public class UploadFile {
private static final Logger LOG = Logger.getLogger(UploadFile.class);
/**
* 上傳文件
*
* @param request
* http request
* @param response
* htp response
* @throws IOException
* IOException
*/
@SuppressWarnings("unchecked")
public static void upload(HttpServletRequest request,
HttpServletResponse response) throws IOException {
if (request.getContentType() == null) {
throw new IOException(
"the request doesn't contain a multipart/form-data stream");
}
// 上傳臨時路徑
String path = GetSharePathXml.getShareFolderPath();
// 設置上傳工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(new File(path));
// 閥值,超過這個值纔會寫到臨時目錄
factory.setSizeThreshold(1024 * 1024 * 10);
ServletFileUpload upload = new ServletFileUpload(factory);
// 最大上傳限制
upload.setSizeMax(1024 * 1024 * 1000);
// 設置監聽器監聽上傳進度
upload.setProgressListener(new FileUploadListener(request));
try {
List<FileItem> items = upload.parseRequest(request);
//獲取文件類型
String variety = request.getParameter("variety");
for (FileItem item : items) {
// 非表單域
if (!item.isFormField()) {
FileOutputStream fos = new FileOutputStream(path + "/" + variety + "/" + item.getName());
// 文件全在內存中
if (item.isInMemory()) {
fos.write(item.get());
} else {
InputStream is = item.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
is.close();
}
fos.close();
LOG.info("完成上傳文件!");
item.delete();
LOG.info("刪除臨時文件!");
LOG.info("更新progress對象狀態爲完成狀態!");
}
}
} catch (Exception e) {
LOG.error("上傳文件出現異常, 錯誤原因 : " + e.getMessage());
request.getSession().removeAttribute("percent");
}
}
}
前臺部分:點擊上傳,然後循環調用
//進度條顯示
var everylisten = function() {
//顯示進度條
$("#prosbar").parent("div").css({"display":"block"});
$.ajax({
url : 'http://127.0.0.1/guideUpload!progress.action',
method : 'GET',
timeout : 120000,
contentType : "application/json; charset=utf-8",
dataType : "json",
success : function(result) {
if(null != result) {
if(result.complete) {
//將進度條長度設爲0並隱藏
$("#prosbar").css({"width":"0%"});
clearTimeout(everylisten);
//清除session
clearProgressSession();
}else{
var width = result.currentLength * 100 / result.length + "%";
$("#prosbar").css({"width": width});
setTimeout(everylisten, 500);
}
}else{
alert(data.msg);
}
}
});
};
清除進度條session
//清除session
function clearProgressSession(){
$.ajax({
url : hik.guide.getContextPath() + '/guideUpload!clearProgressSession.action',
method : 'GET',
timeout : 120000,
contentType : "application/json; charset=utf-8",
dataType : "json",
success : function(result) {
}
});
}
點擊文件上傳時的js代碼(PS:文件上傳時相關參數寫在路徑裏,不然後臺接收不到(如果不寫在路徑裏,測試時在瀏覽器調試發現參數確實傳了,但在request裏這個參數的值爲空),因爲請求地址是要經過tomcat的,爲防止中文亂碼的情況,在tomcat路徑conf文件夾下找到server.xml文件,找到以下項,添加紅色部分的代碼 <ConnectorURIEncoding="UTF-8" connectionTimeout="20000"
port="80" protocol="HTTP/1.1" redirectPort="8443"/> ,保存後重啓tomcat)
$("#GuideForm").submit(function(){
everylisten();
return false;
$.ajaxFileUpload
(
{
url:'http://127.0.0.1/guideUpload!uploadfile.action?'
+ $("#GuideForm").serialize(),
secureuri:false,
fileElementId:'upload',
dataType: 'json',
data:{},
success: function (data, status)
{
$(dialogEl).dialog('close');
//上傳成功後,提示用戶上傳成功
if(data.success){
alert("success");
}else{
alert("false");
}
},
error: function (data, status, e)
{
alert("出錯了");
}
}
)
});
頁面代碼(紅色部分爲必須寫的地方):
<form id="GuideForm" name="guideForm" action=""
οnsubmit="return false;"
class="form-horizontal five-columns character5 padding-top8px"
</span><span style="color:#cc0000;">enctype="multipart/form-data"</span><span style="color:#333333;"> method="post">
<div>
<input type="hidden" name="ids" >
<div class="control-group five-columns">
<label class="control-label">文件名稱:</label>
<div class="controls">
<input class="span4" type="text" name="name" maxlength="20" readonly="readonly"/>
</div>
</div>
<div class="control-group five-columns">
<label class="control-label">文件類型:</label>
<div class="controls">
<select name="variety">
<option value="tools">開發工具</option>
<option value="ecliplse_plugin">Eclipse常用插件</option>
<option value="svn">代碼版本控制SVN</option>
<option value="database">數據庫</option>
<option value="other">其他</option>
</select>
</div>
</div>
<div class="control-group five-columns" style="display:none;">
<label class="control-label">文件路徑:</label>
<div class="controls">
<a name="path" style="color:blue"></a>
</div>
<div class="controls">
<input type="button" class="btn btn-primary uploadReset" value="重新上傳" name="uploadReset">
</div>
</div>
<div class="control-group five-columns">
<div class="controls">
<label class="control-label">上傳文件 :</label>
<input class="span4" type="file"
name="upload" maxlength="20" id="upload" />
</div>
</div>
<div class="control-group five-columns">
<div class="controls">
<label class="control-label">文件備註:</label>
<textarea rows="" cols="" name="comments" maxLength="50"></textarea>
</div>
</div>
<div class="progress progress-striped active" >
<div class="bar" style="width: 0%;" id="prosbar"></div>
</div>
</div>
<!-- 上傳進度條 -->
</form>