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 的头信息里面的 Content­Type 是不是多类型参数 (multipart/form­data)的请求,如果是就把 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参数名。如果直接使用 WebWorkFileUpload 拦截器,推荐使用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】文件上传下载相关推荐

  1. java批量上传文件_Spring Boot2(十四):单文件上传/下载,文件批量上传

    文件上传和下载在项目中经常用到,这里主要学习SpringBoot完成单个文件上传/下载,批量文件上传的场景应用.结合mysql数据库.jpa数据层操作.thymeleaf页面模板. 一.准备 添加ma ...

  2. Django实战之文件上传下载

    项目介绍 最近学习django,通过文件上传下载这个小项目,总结下常用的知识点. 做这个案例我有以下需求: 1.要支持一次上传多个文件 2.支持上传后记录上传的数据以及列表展示 3.支持下载和删除文件 ...

  3. [网络安全学习篇60]:文件上传

    引言:我的系列博客[网络安全学习篇]上线了,小编也是初次创作博客,经验不足:对千峰网络信息安全开源的视频公开课程的学习整理的笔记整理的也比较粗糙,其实看到目录有300多集的时候,讲道理,有点怂了,所以 ...

  4. salesforce 零基础学习(四十二)简单文件上传下载

    项目中,常常需要用到文件的上传和下载,上传和下载功能实际上是对Document对象进行insert和查询操作.本篇演示简单的文件上传和下载,理论上文件上传后应该将ID作为操作表的字段存储,这里只演示文 ...

  5. linux 安装上传文件,linux常用命令(二)文件上传下载及软件安装

    1.上传下载工具安装 (1)WINDOWS 到linux的文件上传及下载: windows下打开secureCRT,通过SSH连到⾄至远程linux主机: 上传下载工具安装命令:yum -y inst ...

  6. [C# 网络编程系列]专题十一:实现一个基于FTP协议的程序——文件上传下载器...

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

  7. ASP.NET中文件上传下载方法集合

    asp.net 2008-08-23 21:10:35 阅读0 评论0   字号:大中小 订阅 ASP.NET中文件上传下载方法集合 文件的上传下载是我们在实际项目开发过程中经常需要用到的技术,这里给 ...

  8. ASP.NET中常用的文件上传下载方法

    ASP.NET中常用的文件上传下载方法 文件的上传下载是我们在实际项目开发过程中经常需要用到的技术,这里给出几种常见的方法,本文主要内容包括: 1.如何解决文件上传大小的限制 2.以文件形式保存到服务 ...

  9. java上传文件到ftp_java实现文件上传下载至ftp服务器

    以前做的一个项目,用到了文件上传下载至ftp服务器,现在对其进行一下复习,比较简单,一下就能看明白. 环境:首先,先安装ftp服务器,我是在win8本地用IIS配置的, 百度一下就可以找到安装文档. ...

最新文章

  1. 弃Java、Swift于不顾,为何选Python?
  2. python中return的理解-python中的return的返回与执行
  3. 优化算法 sklearn lr
  4. [笔记]提升R的性能和突破内存限制的技巧
  5. 图解TCPIP-NAT
  6. pandas 增删改
  7. python 函数写商城管理系统
  8. Pywinauto在Windows Twain Driver自动化测试中的应用研究
  9. Python项目部署到Docker的完整过程
  10. 国内android源码下载方法
  11. SolidWorks2008破解版及安装方法
  12. 银行系统数据流图-盒图-软件结构图对应一例
  13. web im环信陌生人聊天或客服聊天功能
  14. m4s格式转换mp3_mp4怎么转换成mp3格式?mp4转mp3的傻瓜式方法
  15. 《当我谈跑步时我谈些什么》:痛苦难以避免,而磨难可以选择
  16. 集肤效应、邻近效应、边缘效应、涡流损耗
  17. 任务栏广告弹窗源头查找与处理方法
  18. 函数式编程[Lambda 表达式,Optional,Stream流]从入门到精通(一)
  19. 阅读《Detection of EEG-Based Eye-Blinks Using A Thresholding Algorithm》小结
  20. Java第四十八天,Jsp之Taglib,自定义标签详解

热门文章

  1. VOC2012 分割数据 转 lmdb 格式 python 代码
  2. Java 内存泄漏监控检测与分析
  3. python字符编码是什么_python3字符编码是什么?怎么用?
  4. 小工程结算书范本_工程结算合同书范本_工程结算合同格式
  5. java工作流 数据库设计_Java开发网
  6. android os被删除怎么办,手机系统应用误删了怎么办 如何修复手机异常【详细介绍】...
  7. 推荐十一个吊炸天的AI学习项目
  8. Docker(六):Docker 仓库管理与镜像加速
  9. 盒模型,块状元素,行内元素
  10. Core Animation1-简介