struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

上一篇文章讲了上传单个文件与上传多个文件(属性驱动)的例子。本例是上传多个文件(属性驱动),并且显示进度条、进度详细信息的示范。

  • 在文件上传选择界面,允许用户增加、删除选择的文件,且只能上传指定类型的文件;
  • 在文件上传选择界面,不允许用户直接输入文件名,必须通过按钮选择文件;
  • 上传过程中,利用jQuery的progressbar的widget插件显示当前上传进度的百分比;
  • 上传过程中,利用一个DIV显示上传进度的详细信息。

目录

1. 设计上传的JSP页面(uploadTest3.jsp)
  1.1 关于jQuery的进度条,有三段代码
  1.2 轮询后台查询进度
2. 显示处理结果的JSP页面(showResult2.jsp)
3. 创建Action类及属性驱动类
4. 创建实现ProgressListener接口的类、创建实现查询进度的Action类、创建传递进度信息的实体类
5. 将上传进度类与Struts2关联
  5.1 拷贝JakartaMultiPartRequest类并做适当修改
  5.2 替换Struts2默认的上传关联类(修改struts.xml)
6. 修改struts.xml,加入Action配置
7. 设置上传文件相关参数(struts.properties)
8. 测试

1. 设计上传的JSP页面(uploadTest3.jsp)

对比于上一篇文章中的uploadTest2.jsp文件,加入了如下功能:

  • 增加、删除上传文件,只能上传指定类型的文件;只能通过按钮选择文件,不可能直接输入文件名。方法:file_change()/remove_file()/insertFile()/f()等。
  • 增加调用jQuery的AJAX方法轮询后台上传进度信息并显示在前台。方法:refreshProcessBar()/refreshProcessBarCallBack()/及function()初始化进度条等。
  • 增加了引用jQuery进度条(progressbar)的相关CSS与JS文件:jquery.js/jquery.ui.core.js/jquery.ui.widget.js/jquery.ui.progressbar.js;至于CSS文件,为简单起见,我直接将示范目录themes/base/拷贝到项目中。
struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

<%@ page language="java" import="java.util.*" pageEncoding="utf-8" %> 
<%@ page isELIgnored="false" %> 
<%@ taglib uri="/struts-tags" prefix="s" %> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>多文件上传,显示进度条实例</title> 
<style type="text/css"> 
<!-- 
body, td, th { 
font-size: 9pt; 
} 
--> 
</style> 
<!--参考:http://api.jqueryui.com/progressbar/--> 
<link rel="stylesheet" href="../../css/themes/base/jquery.ui.all.css"> 
<script src="../../js/jquery.js"></script> 
<script src="../../js/jquery.ui.core.js"></script> 
<script src="../../js/jquery.ui.widget.js"></script> 
<script src="../../js/jquery.ui.progressbar.js"></script> 
<script type="text/javascript"> 
// 下面这三个函数是生成与刷新进度条、进度详细信息的 
// 初始化进度条 
    $(function() { 
$("#progressbar").progressbar({ 
value: 0 
}); 
}); 
// 调用查询进度信息接口 
function refreshProcessBar(){ 
$.get("getState.action?timestamp=" + new Date().getTime(), function(data){ 
refreshProcessBarCallBack(data); 
}, 'xml'); 
} 
// 查询进度信息接口回调函数 
function refreshProcessBarCallBack(returnXMLParam){ 
var returnXML = returnXMLParam; 
var percent = $(returnXML).find('percent').text() 
var showText = "进度是:" + percent + "%"; 
showText = showText + "/n当前上传文件大小为:" + $(returnXML).find ('uploadByte').text(); 
showText = showText + "/n上传文件总大小为:" + $(returnXML).find ('fileSizeByte').text(); 
showText = showText + "/n当前上传文件为第:" + $(returnXML).find ('fileIndex').text() + ""; 
showText = showText + "/n开始上传时间:" + $(returnXML).find ('startTime').text(); 
// 刷新进度详细信息 
        $('#progressDetail').empty(); 
$('#progressDetail').text(showText); 
// 刷新进度条 
        $("#progressbar").progressbar("option", "value", parseInt(percent)); 
setTimeout("refreshProcessBar()", 1000); 
} 
// 下面这三个函数是控制添加、删除、修改附件的(允许增加、删除附件,只允许指定后缀的文件被选择等) 
var a = 0; 
function file_change(){ 
//当文本域中的值改变时触发此方法 
var postfix = this.value.substring(this.value.lastIndexOf(".") + 1).toUpperCase(); 
//判断扩展是否合法 
if (postfix == "JPG" || postfix == "GIF" || postfix == "PNG" || postfix == "BMP" || 
postfix == "RAR" || 
postfix == "ZIP" || 
postfix == "TXT" || 
postfix == "GHO" || 
postfix == "PDF") { 
} 
else { 
//如果不合法就删除相应的File表单及br标签 
            alert("您上传的文件类型不被支持,本系统只支持JPG,GIF,PNG,BMP,RAR,ZIP,TXT文件!"); 
var testtest = $(this).attr('id'); 
testtest = '#' + testtest; 
var sub_file = $(testtest); 
var next_a_ele = sub_file.next();//取得a标记 
var br1_ele = $(next_a_ele).next();//取得回车 
var br2_ele = $(br1_ele).next();//取得回车 
             
$(br2_ele).remove();//删除回车 
            $(br1_ele).remove();//删除回车 
            $(next_a_ele).remove();//删除a标签 
            $(sub_file).remove(); 
//删除文本域,因为上传的文件类型出错,要删除动态创建的File表单 
return; 
} 
} 
function remove_file(){//删除File表单域的方法 
//删除表单 
var testtest = $(this).val(); 
testtest = '#' + testtest; 
var sub_file = $(testtest); 
var next_a_ele = sub_file.next();//取得a标记 
var br1_ele = $(next_a_ele).next();//取得回车 
var br2_ele = $(br1_ele).next();//取得回车 
         
$(br2_ele).remove();//删除回车 
        $(br1_ele).remove();//删除回车 
        $(next_a_ele).remove();//删除a标签 
        $(sub_file).remove();//删除File标记 
    } 
function f(){ 
//方法名为f的主要作用是不允许在File表单域中手动输入文件名,必须单击“浏览”按钮 
return false; 
} 
function insertFile(){ 
//新建File表单 
var file_array = document.getElementsByTagName("input"); 
var is_null = false; 
//循环遍历判断是否有某一个File表单域的值为空 
for (var i = 0; i < file_array.length; i++) { 
if (file_array[i].type == "file" && file_array[i].name.substring(0, 15) == "fileUploadTools") { 
if (file_array[i].value == "") { 
alert("某一附件为空不能继续添加"); 
is_null = true; 
break; 
} 
} 
} 
if (is_null) { 
return; 
} 
a++; 
//新建file表单的基本信息 
var new_File_element = $('<input>'); 
new_File_element.attr('type', 'file'); 
new_File_element.attr('id', 'uploadFile' + a); 
new_File_element.attr('name', 'fileUploadTools.uploadFile'); 
new_File_element.attr('size', 55); 
new_File_element.keydown(f); 
new_File_element.change(file_change); 
$('#fileForm').append(new_File_element); 
//新建删除附件的a标签的基本信息 
var new_a_element = $('<a>'); 
new_a_element.html("删除附件"); 
new_a_element.attr('id', "a_" + new_File_element.name); 
new_a_element.attr('name', "a_" + new_File_element.name); 
new_a_element.val($(new_File_element).attr('id')); 
new_a_element.attr('href', "#"); 
new_a_element.click(remove_file); 
$('#fileForm').append(new_a_element); 
var new_br_element = $("<br>"); 
$('#fileForm').append(new_br_element); 
var new_br_element = $("<br>"); 
$('#fileForm').append(new_br_element); 
} 
// 提交表单,提交时触发刷新进度条函数 
function submitForm(){ 
setTimeout("refreshProcessBar()", 1000); 
return true; 
} 
</script> 
</head> 
<body> 
<br/> 
<s:form action="uploadT2" method="post" enctype="multipart/form-data" onsubmit="return submitForm()"> 
<table width="818" border="1"> 
<tr> 
<td width="176"> 
<div align="center"> 
用户账号 
</div> 
</td> 
<td width="626"> 
<input type="text" name="fileUploadTools.username" /> 
</td> 
</tr> 
<tr> 
<td> 
<div align="center"> 
用户附件 
<br/> 
<a href="javascript:insertFile()">添加附件</a> 
</div> 
</td> 
<td id="fileForm"> 
<br/> 
</td> 
</tr> 
</table> 
<input type="submit" value="开始上传..." /> 
</s:form> 
<br/> 
<div id="progressbar" style="width:500"></div> 
<br/> 
<div id="progressDetail" class="demo-description"> 
<p>进度详细信息显示于此......</p> 
</div> 
</body> 
</html>

因为代码太长,所以缩起来了。

1.1 关于jQuery的进度条,有三段代码

引入CSS及JS文件

需要注意jquery.ui.all.css这个CSS文件会引用其它CSS文件,然后没止境的引用下去,所以,最好的办法,拷贝整个themes/base目录到项目中最好。

themes/base目录位于jquery-ui-1.10.3.zip包中。

progressbar的API官网:http://api.jqueryui.com/progressbar/

<link rel="stylesheet" href="../../css/themes/base/jquery.ui.all.css"> 
<script src="../../js/jquery.js"></script> 
<script src="../../js/jquery.ui.core.js"></script> 
<script src="../../js/jquery.ui.widget.js"></script> 
<script src="../../js/jquery.ui.progressbar.js"></script>

初始化进度条

    // 初始化进度条 
$(function() { 
$("#progressbar").progressbar({ 
value: 0 
}); 
});

更新进度条

        // 刷新进度条 
$("#progressbar").progressbar("option", "value", parseInt(percent));

1.2 轮询后台查询进度

表单提交时,触发refreshProcessBar()方法,回调refreshProcessBarCallBack()方法,然后再触发refreshProcessBar()方法,,,,,,, 这样可以实时查询上传进度,并更新前台页面。

2. 显示处理结果的JSP页面(showResult2.jsp)

这个页面就是上一篇文章中的页面,一模一样的。为缩小篇幅,代码缩起来。

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 
<%@ page isELIgnored="false"%> 
<%@ taglib uri="/struts-tags" prefix="s"%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html> 
<body> 
您上传的文件名列表: 
<br/> 
<!--对Action中对象(fileUploadTools)包含的数组(uploadFileFileName)的遍历--> 
<s:iterator value="fileUploadTools.uploadFileFileName" status="st"> 
<s:iterator value="fileUploadTools.uploadFileFileName[#st.index]">  
<s:property /> 
<br/> 
</s:iterator> 
</s:iterator> 
<br /> 
</body> 
</html>

3. 创建Action类及属性驱动类

UploadTest2Action.java

这个Action类与上一篇文章中的Action类是一模一样的,为缩小篇幅,代码缩起来。

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

package com.clzhang.struts2.demo12; 
import java.io.IOException; 
import com.opensymphony.xwork2.ActionSupport; 
public class UploadTest2Action extends ActionSupport { 
public static final long serialVersionUID = 1; 
// 声明封装了File上传的FileUploadTools类的实例 
// FileUploadTools类也封装了上传的属性及get和set方法 
private FileUploadTools fileUploadTools = new FileUploadTools(); 
public FileUploadTools getFileUploadTools() { 
return fileUploadTools; 
} 
public void setFileUploadTools(FileUploadTools fileUploadTools) { 
this.fileUploadTools = fileUploadTools; 
} 
@Override 
public String execute() throws IOException { 
fileUploadTools.beginUpload(); 
return SUCCESS; 
} 
}

FileUploadTools.java

这个属性代码类与上一篇文章中的Action类也是一模一样的,为缩小篇幅,代码缩起来。

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

package com.clzhang.struts2.demo12; 
import java.io.File; 
import java.io.IOException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
import org.apache.commons.io.FileUtils; 
import org.apache.struts2.ServletActionContext; 
public class FileUploadTools { 
private String username; 
private File uploadFile[];// 上传的文件是数组类型 
private String uploadFileFileName[];// 文件名是数组类型 
private String uploadFileContentType[]; 
public String getUsername() { 
return username; 
} 
public void setUsername(String username) { 
this.username = username; 
} 
// 上传的ContentType文件类型也是数组类型 
// 必须要加上对ContentType的声明,否则会出现异常 
public String[] getUploadFileContentType() { 
return uploadFileContentType; 
} 
public void setUploadFileContentType(String[] uploadFileContentType) { 
this.uploadFileContentType = uploadFileContentType; 
} 
public File[] getUploadFile() { 
return uploadFile; 
} 
public void setUploadFile(File[] uploadFile) { 
this.uploadFile = uploadFile; 
} 
public String[] getUploadFileFileName() { 
return uploadFileFileName; 
} 
public void setUploadFileFileName(String[] uploadFileFileName) { 
this.uploadFileFileName = uploadFileFileName; 
} 
public String beginUpload() throws IOException { 
System.out.println("用户名:" + username); 
String targetDirectory = ServletActionContext.getServletContext().getRealPath("/upload"); 
for (int i = 0; i < uploadFile.length; i++) { 
File target = new File(targetDirectory, new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss") 
.format(new Date()).toString() + System.nanoTime() + uploadFileFileName[i]); 
FileUtils.copyFile(uploadFile[i], target); 
} 
return "success"; 
} 
}

4. 创建实现ProgressListener接口的类、创建实现查询进度的Action类、创建传递进度信息的实体类

这个是查询进度信息的核心。

  • State类是一个保存进度信息的实体类,属性包括:uploadByte上传了多少字节,fileSizeByte文件总大小,fileIndex正在上传第几个文件等等;
  • FileUploadListener类是一个实现ProgressListener接口的类,会在上传文件时,将接收到的相关信息存储到State类中;
  • GetState是一个Action类,是将State当前状态返回给前台的接口,由前台AJAX调用,所以execute()方法返回null即可。

State.java

package com.clzhang.struts2.demo12; 
import java.text.NumberFormat; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
public class State { 
private long uploadByte;  //已经上传的字节数,单位:字节 
private long fileSizeByte; //所有文件的总长度,单位:字节 
private int fileIndex; //正在上传第几个文件 
private long startTime; //开始上传的时间,用于计算上传速度等 
private int percent; // 上传百分比 
private static final SimpleDateFormat SIMPLEFORMAT = new SimpleDateFormat("HH:mm:ss"); 
public State() { 
startTime = System.currentTimeMillis(); 
percent = 0; 
} 
// 从State状态类中取得状态的字符串,用字符串的形式拼成XML文件内容 
public synchronized String getStateString() { 
StringBuilder sb = new StringBuilder("<info>"); 
sb.append("<uploadByte>" + NumberFormat.getInstance().format(uploadByte) + "</uploadByte>"); 
sb.append("<fileSizeByte>" + NumberFormat.getInstance().format(fileSizeByte) + "</fileSizeByte>"); 
sb.append("<fileIndex>" + fileIndex + "</fileIndex>"); 
sb.append("<percent>" + percent + "</percent>"); 
sb.append("<startTime>" + SIMPLEFORMAT.format(startTime) + "</startTime>"); 
sb.append("</info>"); 
return sb.toString(); 
} 
public synchronized void setState(long uploadByte, long fileSizeByte, int fileIndex) { 
this.uploadByte = uploadByte; 
this.fileSizeByte = fileSizeByte; 
this.fileIndex = fileIndex; 
if ((Long.valueOf(uploadByte) * 100 / Long.valueOf(fileSizeByte) <= 100)) { 
// 生成当前上传进度的公式,加入判断条件的含义在于不需要重复计算 
percent = (int)(Long.valueOf(uploadByte) * 100 / Long.valueOf(fileSizeByte)); 
} 
} 
}

FileUploadListener.java

package com.clzhang.struts2.demo12; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpSession; 
import org.apache.commons.fileupload.ProgressListener; 
public class FileUploadListener implements ProgressListener { 
// 声明一个HttpSession,目的是把State对象放到这个HttpSession中 
private HttpSession session; 
// 此构造函数由MyJakartaMultiPartRequest.java类parseRequest()方法调用 
public FileUploadListener(HttpServletRequest request) { 
super(); 
session = request.getSession(); 
} 
public void update(long uploadByte, long fileSizeByte, int fileIndex) { 
if (fileSizeByte == -1) { 
// 如果上传的大小为-1则上传已经完成 
System.out.println("上传文件结束!"); 
} else { 
if (session.getAttribute("uploadState") == null) { 
// 如果为空就new一个State对象并设置里面的文本内容 
State state = new State(); 
state.setState(uploadByte, fileSizeByte, (fileIndex - 1)); 
session.setAttribute("uploadState", state); 
} else { 
// 如果session中有uploadState对象就取出来,然后设置里面文本内容 
State state = (State) session.getAttribute("uploadState"); 
state.setState(uploadByte, fileSizeByte, (fileIndex - 1)); 
} 
} 
} 
}

GetState.java

package com.clzhang.struts2.demo12; 
import java.io.IOException; 
import org.apache.struts2.ServletActionContext; 
public class GetState { 
public String execute() throws IOException { 
// 从session中取得名称为uploadState的State对象 
State tempState = (State) ServletActionContext.getRequest().getSession() 
.getAttribute("uploadState"); 
// 设置编码为utf-8 
ServletActionContext.getResponse().setCharacterEncoding("utf-8"); 
// 设置响应的格式为XML 
ServletActionContext.getResponse().setContentType("text/xml"); 
// 用out对象输出xml代码头 
        ServletActionContext.getResponse().getWriter() 
.print("<?xml version='1.0' encoding='" + "utf-8" + "' ?>"); 
// 用out对象输出xml代码体 
        ServletActionContext.getResponse().getWriter().print(tempState.getStateString()); 
return null; 
} 
}

5. 将上传进度类与Struts2关联

需要替换掉Struts2封装的上传外包类,以使fileUpload与我们上面的监听器绑定起来。

5.1 拷贝JakartaMultiPartRequest类并做适当修改

将org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest类的源代码拷贝到项目中,更改如下代码:

import org.apache.struts2.dispatcher.multipart.MultiPartRequest; 
private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException { 
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir); 
ServletFileUpload upload = new ServletFileUpload(fac); 
upload.setProgressListener(new FileUploadListener(servletRequest));// 设置上传进度的监听 
        upload.setSizeMax(maxSize); 
return upload.parseRequest(createRequestContext(servletRequest)); 
}

注意代码中的upload.setProgressListener(new FileUploadListener(servletRequest));行,该语句将自定义监听器与上传组件结合了起来。

完整代码如下(struts-2.3.8):

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

/* 
* $Id: JakartaMultiPartRequest.java 1384107 2012-09-12 20:14:23Z lukaszlenart $ 
* 
* Licensed to the Apache Software Foundation (ASF) under one 
* or more contributor license agreements.  See the NOTICE file 
* distributed with this work for additional information 
* regarding copyright ownership.  The ASF licenses this file 
* to you under the Apache License, Version 2.0 (the 
* "License"); you may not use this file except in compliance 
* with the License.  You may obtain a copy of the License at 
* 
*  http://www.apache.org/licenses/LICENSE-2.0 
* 
* Unless required by applicable law or agreed to in writing, 
* software distributed under the License is distributed on an 
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
* KIND, either express or implied.  See the License for the 
* specific language governing permissions and limitations 
* under the License. 
*/ 
package com.clzhang.struts2.demo12; 
import com.opensymphony.xwork2.LocaleProvider; 
import com.opensymphony.xwork2.inject.Inject; 
import com.opensymphony.xwork2.util.LocalizedTextUtil; 
import com.opensymphony.xwork2.util.logging.Logger; 
import com.opensymphony.xwork2.util.logging.LoggerFactory; 
import org.apache.commons.fileupload.FileItem; 
import org.apache.commons.fileupload.FileUploadBase; 
import org.apache.commons.fileupload.FileUploadException; 
import org.apache.commons.fileupload.RequestContext; 
import org.apache.commons.fileupload.disk.DiskFileItem; 
import org.apache.commons.fileupload.disk.DiskFileItemFactory; 
import org.apache.commons.fileupload.servlet.ServletFileUpload; 
import org.apache.struts2.StrutsConstants; 
import javax.servlet.http.HttpServletRequest; 
import java.io.File; 
import java.io.IOException; 
import java.io.InputStream; 
import java.io.UnsupportedEncodingException; 
import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Enumeration; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Locale; 
import java.util.Map; 
import java.util.Set; 
import org.apache.struts2.dispatcher.multipart.MultiPartRequest; 
/** 
* Multipart form data request adapter for Jakarta Commons Fileupload package. 
*/ 
public class MyJakartaMultiPartRequest implements MultiPartRequest { 
static final Logger LOG = LoggerFactory.getLogger(MyJakartaMultiPartRequest.class); 
// maps parameter name -> List of FileItem objects 
protected Map<String, List<FileItem>> files = new HashMap<String, List<FileItem>>(); 
// maps parameter name -> List of param values 
protected Map<String, List<String>> params = new HashMap<String, List<String>>(); 
// any errors while processing this request 
protected List<String> errors = new ArrayList<String>(); 
protected long maxSize; 
private Locale defaultLocale = Locale.ENGLISH; 
@Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE) 
public void setMaxSize(String maxSize) { 
this.maxSize = Long.parseLong(maxSize); 
} 
@Inject 
public void setLocaleProvider(LocaleProvider provider) { 
defaultLocale = provider.getLocale(); 
} 
/** 
* Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's 
* multipart classes (see class description). 
* 
* @param saveDir the directory to save off the file 
* @param request the request containing the multipart 
* @throws java.io.IOException is thrown if encoding fails. 
*/ 
public void parse(HttpServletRequest request, String saveDir) throws IOException { 
try { 
setLocale(request); 
processUpload(request, saveDir); 
} catch (FileUploadBase.SizeLimitExceededException e) { 
if (LOG.isWarnEnabled()) { 
LOG.warn("Request exceeded size limit!", e); 
} 
String errorMessage = buildErrorMessage(e, new Object[]{e.getPermittedSize(), e.getActualSize()}); 
if (!errors.contains(errorMessage)) { 
errors.add(errorMessage); 
} 
} catch (Exception e) { 
if (LOG.isWarnEnabled()) { 
LOG.warn("Unable to parse request", e); 
} 
String errorMessage = buildErrorMessage(e, new Object[]{}); 
if (!errors.contains(errorMessage)) { 
errors.add(errorMessage); 
} 
} 
} 
protected void setLocale(HttpServletRequest request) { 
if (defaultLocale == null) { 
defaultLocale = request.getLocale(); 
} 
} 
protected String buildErrorMessage(Throwable e, Object[] args) { 
String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName(); 
if (LOG.isDebugEnabled()) { 
LOG.debug("Preparing error message for key: [#0]", errorKey); 
} 
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args); 
} 
private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException { 
for (FileItem item : parseRequest(request, saveDir)) { 
if (LOG.isDebugEnabled()) { 
LOG.debug("Found item " + item.getFieldName()); 
} 
if (item.isFormField()) { 
processNormalFormField(item, request.getCharacterEncoding()); 
} else { 
processFileField(item); 
} 
} 
} 
private void processFileField(FileItem item) { 
if (LOG.isDebugEnabled()) { 
LOG.debug("Item is a file upload"); 
} 
// Skip file uploads that don't have a file name - meaning that no file was selected. 
if (item.getName() == null || item.getName().trim().length() < 1) { 
LOG.debug("No file has been uploaded for the field: " + item.getFieldName()); 
return; 
} 
List<FileItem> values; 
if (files.get(item.getFieldName()) != null) { 
values = files.get(item.getFieldName()); 
} else { 
values = new ArrayList<FileItem>(); 
} 
values.add(item); 
files.put(item.getFieldName(), values); 
} 
private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException { 
if (LOG.isDebugEnabled()) { 
LOG.debug("Item is a normal form field"); 
} 
List<String> values; 
if (params.get(item.getFieldName()) != null) { 
values = params.get(item.getFieldName()); 
} else { 
values = new ArrayList<String>(); 
} 
// note: see http://jira.opensymphony.com/browse/WW-633 
// basically, in some cases the charset may be null, so 
// we're just going to try to "other" method (no idea if this 
// will work) 
if (charset != null) { 
values.add(item.getString(charset)); 
} else { 
values.add(item.getString()); 
} 
params.put(item.getFieldName(), values); 
item.delete(); 
} 
private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException { 
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir); 
ServletFileUpload upload = new ServletFileUpload(fac); 
upload.setProgressListener(new FileUploadListener(servletRequest));// 设置上传进度的监听 
        upload.setSizeMax(maxSize); 
return upload.parseRequest(createRequestContext(servletRequest)); 
} 
private DiskFileItemFactory createDiskFileItemFactory(String saveDir) { 
DiskFileItemFactory fac = new DiskFileItemFactory(); 
// Make sure that the data is written to file 
fac.setSizeThreshold(0); 
if (saveDir != null) { 
fac.setRepository(new File(saveDir)); 
} 
return fac; 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames() 
*/ 
public Enumeration<String> getFileParameterNames() { 
return Collections.enumeration(files.keySet()); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String) 
*/ 
public String[] getContentType(String fieldName) { 
List<FileItem> items = files.get(fieldName); 
if (items == null) { 
return null; 
} 
List<String> contentTypes = new ArrayList<String>(items.size()); 
for (FileItem fileItem : items) { 
contentTypes.add(fileItem.getContentType()); 
} 
return contentTypes.toArray(new String[contentTypes.size()]); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String) 
*/ 
public File[] getFile(String fieldName) { 
List<FileItem> items = files.get(fieldName); 
if (items == null) { 
return null; 
} 
List<File> fileList = new ArrayList<File>(items.size()); 
for (FileItem fileItem : items) { 
File storeLocation = ((DiskFileItem) fileItem).getStoreLocation(); 
if (fileItem.isInMemory() && storeLocation != null && !storeLocation.exists()) { 
try { 
storeLocation.createNewFile(); 
} catch (IOException e) { 
if (LOG.isErrorEnabled()) { 
LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(), e); 
} 
} 
} 
fileList.add(storeLocation); 
} 
return fileList.toArray(new File[fileList.size()]); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String) 
*/ 
public String[] getFileNames(String fieldName) { 
List<FileItem> items = files.get(fieldName); 
if (items == null) { 
return null; 
} 
List<String> fileNames = new ArrayList<String>(items.size()); 
for (FileItem fileItem : items) { 
fileNames.add(getCanonicalName(fileItem.getName())); 
} 
return fileNames.toArray(new String[fileNames.size()]); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String) 
*/ 
public String[] getFilesystemName(String fieldName) { 
List<FileItem> items = files.get(fieldName); 
if (items == null) { 
return null; 
} 
List<String> fileNames = new ArrayList<String>(items.size()); 
for (FileItem fileItem : items) { 
fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName()); 
} 
return fileNames.toArray(new String[fileNames.size()]); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String) 
*/ 
public String getParameter(String name) { 
List<String> v = params.get(name); 
if (v != null && v.size() > 0) { 
return v.get(0); 
} 
return null; 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames() 
*/ 
public Enumeration<String> getParameterNames() { 
return Collections.enumeration(params.keySet()); 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String) 
*/ 
public String[] getParameterValues(String name) { 
List<String> v = params.get(name); 
if (v != null && v.size() > 0) { 
return v.toArray(new String[v.size()]); 
} 
return null; 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors() 
*/ 
public List<String> getErrors() { 
return errors; 
} 
/** 
* Returns the canonical name of the given file. 
* 
* @param filename the given file 
* @return the canonical name of the given file 
*/ 
private String getCanonicalName(String filename) { 
int forwardSlash = filename.lastIndexOf("/"); 
int backwardSlash = filename.lastIndexOf("//"); 
if (forwardSlash != -1 && forwardSlash > backwardSlash) { 
filename = filename.substring(forwardSlash + 1, filename.length()); 
} else if (backwardSlash != -1 && backwardSlash >= forwardSlash) { 
filename = filename.substring(backwardSlash + 1, filename.length()); 
} 
return filename; 
} 
/** 
* Creates a RequestContext needed by Jakarta Commons Upload. 
* 
* @param req the request. 
* @return a new request context. 
*/ 
private RequestContext createRequestContext(final HttpServletRequest req) { 
return new RequestContext() { 
public String getCharacterEncoding() { 
return req.getCharacterEncoding(); 
} 
public String getContentType() { 
return req.getContentType(); 
} 
public int getContentLength() { 
return req.getContentLength(); 
} 
public InputStream getInputStream() throws IOException { 
InputStream in = req.getInputStream(); 
if (in == null) { 
throw new IOException("Missing content in the request"); 
} 
return req.getInputStream(); 
} 
}; 
} 
/* (non-Javadoc) 
* @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp() 
*/ 
public void cleanUp() { 
Set<String> names = files.keySet(); 
for (String name : names) { 
List<FileItem> items = files.get(name); 
for (FileItem item : items) { 
if (LOG.isDebugEnabled()) { 
String msg = LocalizedTextUtil.findText(this.getClass(), "struts.messages.removing.file", 
Locale.ENGLISH, "no.message.found", new Object[]{name, item}); 
LOG.debug(msg); 
} 
if (!item.isInMemory()) { 
item.delete(); 
} 
} 
} 
} 
}

5.2 替换Struts2默认的上传关联类(修改struts.xml)

    <!--demo12,struts2文件上传与下载-->     <constant name="struts.multipart.handler" value="MyJakartaMultiPartRequestRef" />      <!--demo12,struts2文件上传与下载-->     <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"         name="MyJakartaMultiPartRequestRef"         class="com.clzhang.struts2.demo12.MyJakartaMultiPartRequest" scope="default"/>

至此,上传文件时将触发我们上面写的FileUploadListener类,捕捉到相关上传进度信息。

6. 修改struts.xml,加入Action配置

        <action name="uploadT2" class="com.clzhang.struts2.demo12.UploadTest2Action"> 
<result>/struts2/demo12/showResult2.jsp</result> 
</action> 
<action name="getState" class="com.clzhang.struts2.demo12.GetState"></action>

第一个Action为提交上传文件表单时用的;第二个Action为轮询上传进度信息用的。

7. 设置上传文件相关参数(struts.properties)

同前一篇文章。

struts.multipart.maxSize=2048000000 
struts.multipart.saveDir=/upload

8. 测试

打开IE,输入地址:http://127.0.0.1:8080/st/struts2/demo12/uploadTest3.jsp

结果如下:

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

选择几个较大的文件,提交:

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

最终结果:

struts2:上传多个文件时实现带进度条、进度详细信息的示范详解编程语言

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/7983.html

(0)
上一篇 2021年7月18日
下一篇 2021年7月18日

相关推荐

发表回复

登录后才能评论