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

  • 在文件上传选择界面,允许用户增加、删除选择的文件,且只能上传指定类型的文件;
  • 在文件上传选择界面,不允许用户直接输入文件名,必须通过按钮选择文件;
  • 上传过程中,利用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/拷贝到项目中。
<%@ 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)

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

<%@ 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类是一模一样的,为缩小篇幅,代码缩起来。

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类也是一模一样的,为缩小篇幅,代码缩起来。

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):

/*
* $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=
struts.multipart.saveDir=/upload

8. 测试

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

结果如下:

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

最终结果:

struts2:上传多个文件时实现带进度条、进度详细信息的示范的更多相关文章

  1. 使用ftp软件上传下载php文件时换行丢失bug

    正 文:   在使用ftp软件上传下载php源文件时,我们偶尔会发现在本地windows下notepad++编辑器写好的php文件,在使用ftp上传到linux服务器后,php文件的换行符全部丢失了, ...

  2. 使用ftp软件上传下载php文件时换行丢失bug(全部变为一行)

    文章来源:http://www.piaoyi.org/computer/ftp-php-r-n-bug.html 正 文: 在使用ftp软件上传下载php源文件时,我们偶尔会发现在本地windows下 ...

  3. ****使用ftp软件上传下载php文件时换行符丢失bug

    在使用ftp软件上传下载php源文件时,我们偶尔会发现在本地windows下notepad++编辑器写好的php文件,在使用ftp上传到linux服务器后,php文件的换行符全部丢失了,导致php文件 ...

  4. spring mvc文件上传(单个文件上传|多个文件上传)

    单个文件上传spring mvc 实现文件上传需要引入两个必须的jar包    1.所需jar包:                commons-fileupload-1.3.1.jar       ...

  5. java+上传后的文件展示

    文件夹结构支持 大文件上传控件6支持向服务器上传整个文件夹,并且在服务端保存时与本地目录结构完全保持一致,同时在数据库中也保留文件夹的层级结构.开发人员可以借助于数据库中的层级信息方便的管理文件,管理 ...

  6. 利用struts2上传文件时,如果文件名中含有-符号,那么会出错

    利用struts2上传文件时,如果文件名中含有-符号,那么会出错 报错如下: HTTP Status 500 - C:\Program Files\Apache Software Foundation ...

  7. struts2实现文件上传、多文件上传和文件下载

    总结的两个问题,就是struts2上传下载的时候对属性名配置要求非常严格: 第一:上传的时候 private File file; private String fileContentType; pr ...

  8. [JavaWeb基础] 009.Struts2 上传文件

    在web开发中,我们经常遇到要把文件上传下载的功能,这篇文章旨在指导大家完成文件上传功能 1.首先我们需要一个上传文件的页面. <!--在进行文件上传时,表单提交方式一定要是post的方式, 因 ...

  9. Unable to find ‘struts.multipart.saveDir’ Struts2上传文件错误的解决方法

    Unable to find ‘struts.multipart.saveDir’ Struts2上传文件错误的解决方法 在使用struts2的项目中上传文件的时候出现了一个这样的错误: 2011-7 ...

随机推荐

  1. haskell中的monad

    monad本意是单子.在haskell中,第一个接触的基本都是IO action,通过把IO动作包装起来我们能很方便的与现实世界进行数据交换.但其实monad的用途不止如此,monad还能讲一系列操作 ...

  2. Android View 如何绘制

    上文说道了Android如何测量,但是一个漂亮的控件我只知道您长到哪儿,这当然不行.只需要简单重写OnDraw方法,并在Canvas(画布)对象上调用那根五颜六色的画笔就能够画出这控件"性感 ...

  3. 关于QT的系统总结

    编译环境与开发流程 开发QT有两种IDE可以使用,一种是使用 VS + Qt 的插件,另一种就是使用QtCreator工具.前一种是微软的工具,用的都比较多容易上手,缺点是信号槽的支持不太好,需要手写 ...

  4. 了解linux下RAID(磁盘阵列)创建和管理

    现在的操作系统,不论是windows 还是linux都具有raid的功能,RAID 分为硬件 RAID 和软件 RAID, 硬件 RAID 是通过 RAID 卡来实现的,软件RAID是通过软件实现的, ...

  5. console.log 被重写覆盖以后如何恢复

    有时候一些项目中会使用类似如下的方式覆盖掉console对象: var console = {}; console.log = function(){}; console.info = functio ...

  6. 真实世界:使用WCF扩展在方法调用前初始化环境

    OperationInvoker 介绍 OperationInvoker 是 WCF 运行时模型中在调用最终用户代码前的最后一个扩展点,OperationInvoker 负责最终调用 Service ...

  7. LoadTest内存和线程Troubleshooting实战

    在端午节放假的三天中,我对正在开发的 Service 进行了 LoadTest,尝试在增大压力的条件下发现问题. 该 Service 为独立进程的 WCF 服务,宿主于 WindowsService, ...

  8. ASP.NET 5系列教程 (六): 在 MVC6 中创建 Web API

    ASP.NET 5.0 的主要目标之一是统一MVC 和 Web API 框架应用. 接下来几篇文章中您会了解以下内容: ASP.NET MVC 6 中创建简单的web API. 如何从空的项目模板中启 ...

  9. ASP.NET中GridView控件删除数据的两种方法

      今天在用GridView控件时,发现了一个问题,就是使用GridView控件在删除数据时的问题.接下来我们通过模板列方式和CommandField方式删除某条数据讲解下两者之间的区别. 方式一:通 ...

  10. C#与数据库访问技术总结(九)之实例

    实例 更新记录 在本例子中,建立一个供用户输入学生学号和姓名的文本框和几个对应不同操作类型的更新信息按钮,当用户输入信息以后单击相应的按钮则执行相应的操作.在此实例中还将接触到服务器信息验证的相关知识 ...