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

  • 在文件上传选择界面,允许用户增加、删除选择的文件,且只能上传指定类型的文件;
  • 在文件上传选择界面,不允许用户直接输入文件名,必须通过按钮选择文件;
  • 上传过程中,利用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=2048000000 
struts.multipart.saveDir=/upload

8. 测试

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

结果如下:

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

最终结果:

发布评论

分享到:

IT虾米网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

JAVA每月运势api调用代码实例详解
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。