Webwork 学习之路【07】文件上传下载
Web上传和下载应该是很普遍的一个需求,无论是小型网站还是大并发访问的交易网站。WebWork 当然也提供了很友好的拦截器来实现对文件的上传,让我们可以专注与业务逻辑的设计和实现,在实现上传和下载时顺便关注了下框架上传下载的实现,在本篇博文中总结记录如下。

1. 包装 Request 请求
- 每次客户端请求 Action 时,都会调用 WebWork 调度类 ServletDispatcher.service()方法。
具体过程请参照: http://www.cnblogs.com/java-class/p/5155793.html
    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
        try {
            if (encoding != null) {
                try {
                    request.setCharacterEncoding(encoding);
                } catch (Exception localException) {
                }
            }
            if (locale != null) {
                response.setLocale(locale);
            }
            if (this.paramsWorkaroundEnabled) {
                request.getParameter("foo");
            }
            request = wrapRequest(request); //封装 request请求
            serviceAction(request, response, getNameSpace(request), getActionName(request), getRequestMap(request), getParameterMap(request), getSessionMap(request), getApplicationMap());
        } catch (IOException e) {
            String message = "Could not wrap servlet request with MultipartRequestWrapper!";
            log.error(message, e);
            sendError(request, response, 500, new ServletException(message, e));
        }
    }
先来看看 wrapRequest 方法做了什么:
    protected HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
        if ((request instanceof MultiPartRequestWrapper)) {
            return request;
        }
        if (MultiPartRequest.isMultiPart(request)) {
            request = new MultiPartRequestWrapper(request, getSaveDir(), getMaxSize());
        }
        return request;
    }
- 首先判断了传进来的 request 是不是已经被封装好的 MultiPartRequestWrapper,如果是就直接返回。
- 否则再判断 request 的头信息里面的 ContentType 是不是多类型参数 (multipart/formdata)的请求,如果是就把 request 包装成 WebWork 自己的 MultiPartRequestWrapper 类型,该类型继承HttpServletRequestWrapper 。
MultiPartRequest.isMultiPart() 方法实现如下:
 public static boolean isMultiPart(HttpServletRequest request){
        String content_type = request.getHeader("Content-Type");
        return content_type != null && content_type.startsWith("multipart/form-data");
    }
- 在 webwork.properties 里面配置文件的临时存储目录、还有最大上传大小,其实就是在这个时候起到作用的。
- 创建 MultiPartRequestWrapper 对象的时候传入的参数是由 getSaveDir() 和 getMaxSize() 方 法 获 得 的 。
- 在方法里会查找配置里面的 webwork.multipart.saveDir、 webwork.multipart.maxSize 属性,找到则使用该属性指定的临时目录和上传的最大内容,没找到就使用环境的临时目录。
具体实现如下:
    protected String getSaveDir() {
        String saveDir = Configuration.getString("webwork.multipart.saveDir").trim();
        if (saveDir.equals("")) {
            File tempdir = (File) getServletConfig().getServletContext().getAttribute("javax.servlet.context.tempdir");
            log.info("Unable to find 'webwork.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
            if (tempdir != null) {
                saveDir = tempdir.toString();
            }
        } else {
            File multipartSaveDir = new File(saveDir);
            if (!multipartSaveDir.exists()) {
                multipartSaveDir.mkdir();
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("saveDir=" + saveDir);
        }
        return saveDir;
    }
2. 获取文件上传的解析类
再来看一下 MultiPartRequestWrapper 的构造函数使如何包装 request 的:
    public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize) throws IOException {
        super(request);
        if ((request instanceof MultiPartRequest)) {
            this.multi = ((MultiPartRequest) request);
        } else {
            String parser = "";
            parser = Configuration.getString("webwork.multipart.parser");
            if (parser.equals("")) {
                log.warn("Property webwork.multipart.parser not set. Using com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest");
                parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest";
            } else if (parser.equals("pell")) {
                parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest";
            } else if (parser.equals("cos")) {
                parser = "com.opensymphony.webwork.dispatcher.multipart.CosMultiPartRequest";
            } else if (parser.equals("jakarta")) {
                parser = "com.opensymphony.webwork.dispatcher.multipart.JakartaMultiPartRequest";
            }
            try {
                Class baseClazz = MultiPartRequest.class;
                Class clazz = Class.forName(parser);
                if (!baseClazz.isAssignableFrom(clazz)) {
                    addError("Class '" + parser + "' does not extend MultiPartRequest");
                    return;
                }
                Constructor ctor = clazz.getDeclaredConstructor(new Class[] { Class.forName("javax.servlet.http.HttpServletRequest"), String.class, Integer.TYPE });
                Object[] parms = { request, saveDir, new Integer(maxSize) };
                this.multi = ((MultiPartRequest) ctor.newInstance(parms));
            } catch (ClassNotFoundException e) {
                addError("Class: " + parser + " not found.");
            } catch (NoSuchMethodException e) {
                addError("Constructor error for " + parser + ": " + e);
            } catch (InstantiationException e) {
                addError("Error instantiating " + parser + ": " + e);
            } catch (IllegalAccessException e) {
                addError("Access errror for " + parser + ": " + e);
            } catch (InvocationTargetException e) {
                addError(e.getTargetException().toString());
            }
        }
    }
- 首先它判断了传入的 request 是不是 MultiPartRequest 抽象类的子类,如果是就直接把自身的 MultiPartRequest 类型的变量引用 request。
- 否则读取 WebWork 配置 的webwork.multipart.parser 属性,该属性决定 Webwork 内部用什么实现文件上传。 如果没有指定,则默认使用 PellMultiPartRequest 的实现。
- 找到配置的类后,WebWork 通过 Java 反射创建该类的实例。所有支持的类都是从 MultiPartRequest 继承而来,创建该实例后向上转型,并赋予 MultiPartRequestWrapper 的成员multi。
- WebWork 的文件上传封装了几种通用的 FileUpload lib,并不是自己实现的。
- 它包括了pell,cos,apache common 三种实现,WebWork 对这三个包进行封装,提供了一 个通用的访问接口 MultiPartRequest,细节实现分别是 PellMultiPartRequest、 CosMultiPartRequest 、JakartaMultiPartRequest 。
- jakarta 支持多个文件使用同一个HTTP参数名。如果直接使用 WebWork 的 FileUpload 拦截器,推荐使用pell,因为当你上传中文文件名称的文件的时候,只有pell 包会正确的获得中文文件名称,apache common会将文件名称改为xxx.tmp这样的文件名,而cos会乱码, 因此我们唯一的选择只有 pell。
- cos 的功能比较强大,WebWork 的封装却使它丧失了很多的功能,cos 需要设置 request 的character encoding。WebWork的封装没有设置,所以就导致了cos的乱码问题,当然如果你单独 使用cos,则会避免此类问题。
3. 项目实战配置和使用
- 配置文件
action 配置:
<action name="uploadAttach" class=".....attach.action.uploadAttach" caption="上传附件">
<result name="success" type="dispatcher">
<param name="location">/result.jsp</param>
</result>
<result name="error" type="dispatcher">
<param name="location">/result.jsp</param>
</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="fileUploadStack" /> //webwok 上传所需要的拦截栈
</action> //拦截栈的定义
<interceptor-stack name="fileUploadStack">
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="params"/>
</interceptor-stack> //拦截栈对应的拦截器
<interceptor name="fileUpload" class="com.opensymphony.webwork.interceptor.FileUploadInterceptor"/>
<interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
- 前端使用比较稳定、功能比较强大的 Ajaxupload这里就不多说了,有官方网址:http://www.cnblogs.com/dabaopku/archive/2011/06/29/2092833.html
- 通过对 Webwork 上传请求的封装和解析类的获取,所有的前戏都已经准备妥当,上传拦截器里面具体实现如下:
public String intercept(ActionInvocation invocation) throws Exception {if (!(ServletActionContext.getRequest() instanceof MultiPartRequestWrapper)) {
            if (log.isDebugEnabled()) {
                log.debug("bypass " + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName());
            }
            return invocation.invoke();
        }
        Action action = invocation.getAction();
        ValidationAware validation = null;
        if ((action instanceof ValidationAware)) {
            validation = (ValidationAware) action;
        }
        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) ServletActionContext.getRequest();
        if (multiWrapper.hasErrors()) {
            Collection errors = multiWrapper.getErrors();
            Iterator i = errors.iterator();
            while (i.hasNext()) {
                String error = (String) i.next();
                if (validation != null) {
                    validation.addActionError(error);
                }
                log.error(error);
            }
        }
        Enumeration e = multiWrapper.getFileParameterNames();
        while ((e != null) && (e.hasMoreElements())) {
            String inputName = (String) e.nextElement();
            String[] contentType = multiWrapper.getContentTypes(inputName);
            String[] fileName = multiWrapper.getFileNames(inputName);
            File[] file = multiWrapper.getFiles(inputName);
            if (file != null) {
                for (int i = 0; i < file.length; i++) {
                    log.info("file " + inputName + " " + contentType[i] + " " + fileName[i] + " " + file[i]);
                }
            }
            if (file == null) {
                if (validation != null) {
                    validation.addFieldError(inputName, "Could not upload file(s). Perhaps it is too large?");
                }
                log.error("Error uploading: " + fileName);
            } else {
                invocation.getInvocationContext().getParameters().put(inputName, file);
                invocation.getInvocationContext().getParameters().put(inputName + "ContentType", contentType);
                invocation.getInvocationContext().getParameters().put(inputName + "FileName", fileName);
            }
        }
        String result = invocation.invoke();
        for (Enumeration e1 = multiWrapper.getFileParameterNames(); e1 != null && e1.hasMoreElements();) {
            String inputValue = (String) e1.nextElement();
            File file[] = multiWrapper.getFiles(inputValue);
            for (int i = 0; i < file.length; i++) {
                File f = file[i];
                log.info("removing file " + inputValue + " " + f);
                if (f != null && f.isFile())
                    f.delete();
            }
        }
        return result;
    }
- 首先判断当前请求是否为 包含多媒体请求,如果是则记录日志,并执行 Action。
- 然后判断在文件上传过程 MultiPartRequestWrapper 是否含有错误,将错误信息,返回给客户端,不在继续调用 Action。
- 如果上面的判断条件都没有进行,开始遍历 MultiPartRequestWrapper 中的上传文件的参数,并将其中的文件名、文件内容类型放入Action 参数 map 中,供后面的业务类进行操作。
- 在 WebWork 的 fileupload 拦截器功能中,它提供的 File只 是一个临时文件,Action 执行之后就会被自动删除,你必须在 Action中自己处理文件的存储问题,或者写到服务器的某个目录,或者保存到数据库中。如果你准备写到服务器的某个目录下面的话,你必须自己面临着处理文件同名的问题,但是实际上cos包已经提供了 文件重名的自动重命名规则。
Webwork 学习之路【07】文件上传下载的更多相关文章
- SpringMVC学习笔记八:文件上传下载(转)
		转自:http://www.cnblogs.com/WJ-163/p/6269409.html 一.关键步骤 ①引入核心JAR文件 SpringMVC实现文件上传,需要再添加两个jar包.一个是文件上 ... 
- Spring Boot2(十四):单文件上传/下载,文件批量上传
		文件上传和下载在项目中经常用到,这里主要学习SpringBoot完成单个文件上传/下载,批量文件上传的场景应用.结合mysql数据库.jpa数据层操作.thymeleaf页面模板. 一.准备 添加ma ... 
- Webwork【07】文件上传下载
		Web上传和下载应该是很普遍的一个需求,无论是小型网站还是大并发访问的交易网站.WebWork 当然也提供了很友好的拦截器来实现对文件的上传,让我们可以专注与业务逻辑的设计和实现,在实现上传和下载时顺 ... 
- Selenium2学习-039-WebUI自动化实战实例-文件上传下载
		通常在 WebUI 自动化测试过程中必然会涉及到文件上传的自动化测试需求,而开发在进行相应的技术实现是不同的,粗略可划分为两类:input标签类(类型为file)和非input标签类(例如:div.a ... 
- Linux学习笔记(7)CRT实现windows与linux的文件上传下载
		Linux学习笔记(7)CRT实现windows与linux的文件上传下载 按下Alt + p 进入SFTP模式,或者右击选项卡进入 命令介绍 help 显示该FTP提供所有的命令 lcd 改变本地上 ... 
- 【Java Web开发学习】Spring MVC文件上传
		[Java Web开发学习]Spring MVC文件上传 转载:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上传有两种实现方式,都比较 ... 
- salesforce 零基础学习(四十二)简单文件上传下载
		项目中,常常需要用到文件的上传和下载,上传和下载功能实际上是对Document对象进行insert和查询操作.本篇演示简单的文件上传和下载,理论上文件上传后应该将ID作为操作表的字段存储,这里只演示文 ... 
- 艺萌文件上传下载及自动更新系统(基于networkComms开源TCP通信框架)
		1.艺萌文件上传下载及自动更新系统,基于Winform技术,采用CS架构,开发工具为vs2010,.net2.0版本(可以很容易升级为3.5和4.0版本)开发语言c#. 本系统主要帮助客户学习基于TC ... 
- NetworkComms 文件上传下载和客户端自动升级(非开源)
		演示程序下载地址:http://pan.baidu.com/s/1geVfmcr 淘宝地址:https://shop183793329.taobao.com 联系QQ号:3201175853 许可:购 ... 
随机推荐
- Java字符串中常见的10个问题
			下面是Java中10个最常见的关于字符串的问题. 怎样比较字符串?使用==还是equals() 简单的说,“==”用于判断引用是否相等,equals()用于判断值是否相等.除非你要比较两个字符串是否是 ... 
- asp.net 导出Excel
			分享一个asp.net 导出假Excel代码.优点,不用借助于任何插件比如(NPOI),复制代码,修改grid.DataSource直接导出. 先看导出后的效果图 System.Web.UI.WebC ... 
- Android ScrollView中的组件设置android:layout_height="fill_parent"不起作用的解决办法
			例子,在ScrollView下加入的组件,无论如何也不能自动扩展到屏幕高度. 布局文件. [html] <?xml version="1.0" encoding=" ... 
- TFS - 使用微软测试管理器实现跨团队项目的测试用例管理
			在团队项目之间实现测试用例和测试计划的共享,是很多客户关注的问题.尤其在开发产品+服务的团队中,对测试用例的共享要求比较高.下面就如何在Team Foundation Server中如何实现团队项目之 ... 
- 我懒蛋又回来了!-PDO
			hi 好几天了吧,脚伤都有一周了的.玩乐的这么久才发觉,对于年轻人,或者更具体的,对我而言,受伤最难受的不是受伤瞬间的身痛,不是随之而来的心理负担,不是独自一人远在他乡的孤独无助之感:最伤的是斗志,是 ... 
- Flappy Bird (Java实现)
			运行效果: 代码: package bird; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; im ... 
- POJ3260The Fewest Coins[背包]
			The Fewest Coins Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6299 Accepted: 1922 ... 
- POJ1270 Following Orders[拓扑排序所有方案 Kahn]
			Following Orders Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 4885 Accepted: 1973 ... 
- sql执行效率,explain 查询执行效率
			1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ... 
- u3d_shader_surface_shader_4
			Rim Lighting 轮廓自发光 一:疑问:1.总感觉在编辑器Scene状态下,脚本计算的ViewDir是我漫游的Cam,而不是项目中的MainCam啊! 然后就会造成Scene状态下轮廓自发光 ... 
