读书人

struts2 下传及进度条显示

发布时间: 2012-12-22 12:05:06 作者: rapoo

struts2 上传及进度条显示

1、struts2上传

以下是一个上传涉及的代码,从view到action的各个阶段代码:

页面:

<form action="../upload/upload.action" id="exForm" method="post" enctype="multipart/form-data" >        <ul>            <li>                <input type="file" name="uploadFile" size="20"/>                <input type="submit" value="导入"/>            </li>        </ul></form>

?

?action代码:

public class UploadFileAction extends ActionSupport {    private static final long serialVersionUID = 5563083429360573304L;    /** 上传文件 */    private File                uploadFile;    private String            uploadFileFileName;    private String            uploadFileContentType;    public String upload() {        try {            //这里是上传文件,可做具体处理            IOUtils.copy(new FileInputStream(uploadFile), new FileOutputStream(new File("C:\\" + uploadFileFileName)));            System.out.println(uploadFileFileName);            System.out.println(uploadFileContentType);        } catch (Exception e) {            e.printStackTrace();        }        return SUCCESS;    }}

?

?配置文件就不再描述。以上就是在struts2中上传涉及的代码,同时还可以支持同时上传多个文件:

支持同时上传多个文件:

<form action="../upload/upload.action" id="exForm" method="post" enctype="multipart/form-data" >        <ul>            <li>                <input type="file" name="uploadFiles" size="20"/><input type="file" name="uploadFiles" size="20"/><input type="file" name="uploadFiles" size="20"/>                <input type="submit" value="导入"/><input type="input" name="message"/>            </li>        </ul></form>

和action代码:

public class UploadFileAction extends ActionSupport {    private static final long serialVersionUID = 5563083429360573304L;    /** 上传文件 */    private File[]            uploadFiles;    private String[]          uploadFilesFileName;    private String[]          uploadFilesContentType;    public String upload() {        try {            int index = 0;            for (File file : uploadFiles) {                IOUtils.copy(new FileInputStream(file), new FileOutputStream(new File("C:\\"                        + uploadFilesFileName[index])));                System.out.println(uploadFilesFileName[index]);                System.out.println(uploadFilesContentType[index]);                index++;            }        } catch (Exception e) {            e.printStackTrace();        }        return SUCCESS;    }}

?

?

2、几点说明

1、在上面上传过程中,页面file域属性名与action中File保持一致。如上:

name="uploadFile" 和private File            uploadFile

多个文件需要是数组。

?

2、之所以如此简单,得益于struts2中默认package:struts-default中

<interceptor name="fileUpload" height="357" src="/img/2012/11/11/111037374.jpg" width="521">

这里提供了对文件、文件名、contentType等属性获取的封装,当然也有对普通表单字段的封装,即如果在上传的表单中有非file域也统一可以获取,而这在普通的servlet上传中并不能简单的处理,如上面的多个文件上传表单中的message,只需要在action中有相应的getter/setter即可获取这些属性。而完成这一切的是一个叫MultiPartRequest的家伙。

?

3、MultiPartRequest

这是上传拦截器中处理封装的具体类,这涉及struts2中上传的底层具体实现,在这个接口中,我们能看到struts2中提供了一个默认的实现:

public class JakartaMultiPartRequest implements MultiPartRequest

在struts2的default.properties中解析有如下说明:

### Parser to handle HTTP POST requests, encoded using the MIME-type multipart/form-data# struts.multipart.parser=cos# struts.multipart.parser=pellstruts.multipart.parser=jakarta# uses javax.servlet.context.tempdir by defaultstruts.multipart.saveDir=struts.multipart.maxSize=2097152

这里对MIME-type为multipart/form-data的几种支持,而默认为jakarta,即在使用struts2的上传,需要添加相应的依赖(版本自行控制):

<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.2.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>1.3.2</version></dependency>
??

4、关于file、filename、contentType

前面已经说了,在action中的File需要和也没file域中的名字保持一致,如果多个则是File数组。而如果要获取fileName以及相应的contentType,命名也有一定的限制,如上面的action中:

    private File[]            uploadFiles;    private String[]          uploadFilesFileName;    private String[]          uploadFilesContentType;

?而这里的命名来自FileUploadInterceptor中对MultiPartRequestWrapper解析完放置于ActionContext中所作的处理:

见MultiPartRequestWrapper中:

if (files != null && files.length > 0) {                        List<File> acceptedFiles = new ArrayList<File>(files.length);                        List<String> acceptedContentTypes = new ArrayList<String>(files.length);                        List<String> acceptedFileNames = new ArrayList<String>(files.length);                        String contentTypeName = inputName + "ContentType";                        String fileNameName = inputName + "FileName";                        for (int index = 0; index < files.length; index++) {                            if (acceptFile(action, files[index], fileName[index], contentType[index], inputName, validation, ac.getLocale())) {                                acceptedFiles.add(files[index]);                                acceptedContentTypes.add(contentType[index]);                                acceptedFileNames.add(fileName[index]);                            }                        }                        if (!acceptedFiles.isEmpty()) {                            Map<String, Object> params = ac.getParameters();                            params.put(inputName, acceptedFiles.toArray(new File[acceptedFiles.size()]));                            params.put(contentTypeName, acceptedContentTypes.toArray(new String[acceptedContentTypes.size()]));                            params.put(fileNameName, acceptedFileNames.toArray(new String[acceptedFileNames.size()]));                        }                    }

?

5、上传进度条显示

在上面第三点已经说明,在struts2中,默认的实现是JakartaMultiPartRequest,这是基于common-fileupload的实现。要处理上传进度条,在common-fileupload中有提供监听上传进度的接口:

/** * The {@link ProgressListener} may be used to display a progress bar * or do stuff like that. */public interface ProgressListener {    /** Updates the listeners status information.     * @param pBytesRead The total number of bytes, which have been read     *   so far.     * @param pContentLength The total number of bytes, which are being     *   read. May be -1, if this number is unknown.     * @param pItems The number of the field, which is currently being     *   read. (0 = no item so far, 1 = first item is being read, ...)     */    void update(long pBytesRead, long pContentLength, int pItems);}

?根据在网上找到的一些资料,这里做了一个实现。首先需要实现该接口,获取显示的进度,一般我们按照百分比来计算,可以在实现的处理监听过程中将该值写入struts2的ActionContent中:

public class ResourceProgressListener implements ProgressListener {    public ResourceProgressListener(HttpServletRequest request) {    }    public void update(long readedBytes, long totalBytes, int currentItem) {        String process = readedBytes * 1.0 / totalBytes * 100 + "";        ActionContext.getContext().getSession().put("currentUploadStatus", process);    }}

?接下来需要替换掉struts2中默认的实现,并在自己的实现中将该监听接口注册进去。在struts2-core-2.2.1.jar这个版本中,找到默认实现类JakartaMultiPartRequest,我们可以将代码全部复制过来,在这个版本的处理中,我们可以看到直接将上传请求用common-fileupload处理是在方法parseRequest中。比如我们将自己的实现叫MultiPartProcessRequest,代码将默认实现JakartaMultiPartRequest中拷贝过来,替换下面的方法:

    private List<FileItem> parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {        DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);        ServletFileUpload upload = new ServletFileUpload(fac);        upload.setSizeMax(maxSize);        //--------------begin add process Listener-----------------//        ResourceProgressListener progressListener = new ResourceProgressListener(servletRequest);//新建一个监听器        upload.setProgressListener(progressListener);//添加自己的监听器        //-------------- end add process Listener-----------------//        return upload.parseRequest(createRequestContext(servletRequest));    }

?上面注释部分是对默认实现的修改,增加了监听。下面需要在struts2中修改默认配置为当前自己的实现,一般我们将该参数配置在struts.xml:

<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="requestParser" scope="default" optional="true" /><constant name="struts.multipart.handler" value="requestParser" />

?到此上传进度监听就处理完成,接下来需要处理在页面的实现:在提交上传的form表单后,将定时调用Ajax请求去读取ActionContext中的进度信息,并在页面显示该实时进度。这里需要在上传的Action中新增一个action方法,读取实时进度,并Ajax返回,都是一些常规操作:

UploadFileAction:

    /**     * Ajax get current process info     * @return     * @author Administrator     * @date 2012-6-3     */    public String process() {        process = (String) ActionContext.getContext().getSession().get("currentUploadStatus");        return SUCCESS;    }

?在struts-upload.xml中返回json:

<!-- process --><action name="process" method="process"><result name="success" type="json"><param name="ignoreHierarchy">true</param><param name="excludeNullProperties">true</param><param name="root">process</param></result></action> 

?接下来,就是在页面如何显示。这里做得很粗糙,只是将Ajax读取的数据展现。这里用到了javascript的定时函数setInterval。但是有个问题,在js中提交form表单中会阻塞该页面请求进程,就不会去请求setInterval中的方法。这里用到了jquery.form.js来异步提交表单,在提交表单后去定时获取后台进度信息,并在页面显示。同时,在表单提交完成后需要停止该定时任务。

<script type="text/javascript" src="../resources/js/jquery-1.4.2.min.js"></script><script type="text/javascript" src="../resources/js/jquery.form.js"></script><script type="text/javascript">$(function () {//这个是为了在上传完成后停止该定时任务,或者有更好的办法?window.processBar = function(){return setInterval(function(){                                                if(isExNew){                                                    jQuery.ajax({                                                            type: "POST",                                                            cache: false,                                                            url: "http://localhost:8080/sa-web/upload/process.action",                                                            data: {},                                                            dataType: "json",                                                            success: function (data) {$("#processId").append(data).append(",");}                                                        })}                                                }, 1000);};//异步提交Form表单,在ajaxForm回调函数中去实时查询最新进度    window.fnExNew = function () {window.isExNew = true;$('#exForm').ajaxForm(function(){setInterval(function(){                                                if(isExNew){                                                    jQuery.ajax({                                                            type: "POST",                                                            cache: false,                                                            url: "http://localhost:8080/sa-web/upload/process.action",                                                            data: {},                                                            dataType: "json",                                                            success: function (data) {$("#processId").append(data).append(",");}                                                        })}                                                }, 1000)});     };    //上传完成的回调方法,    window.fnEx = function (p) {    if(isExNew){    alert("提示", "上传完成");    }else{        alert("提示", p);    }window.isExNew = false;clearInterval(processBar);    };});</script>

?至于回调函数fnEx是在UploadFileAction中upload上传完成处理后返回一个页面,只有一句话那就是调用该方法:

<script>try{parent.fnEx("$!message");}catch(e){}</script>

?到此为止整个简单的展现处理已经完成,当然在实际生产中这样的展现可不行,需要显示进度条可在ajaxForm提交的回调中弹出DIV调用一些jquery组件直观显示,这是后话不表

?

1 楼 jackyrong 2012-08-22 HI,能否提供完整工程下载学习呢?谢谢,jackyrong@tom.com

读书人网 >编程

热点推荐