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

1. 包装 Request 请求

  • 每次客户端请求 Action 时,都会调用 WebWork 调度类 ServletDispatcher.service()方法。

具体过程请参照: http://www.cnblogs.com/java-class/p/5155793.html

  1. public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException {
  2. try {
  3. if (encoding != null) {
  4. try {
  5. request.setCharacterEncoding(encoding);
  6. } catch (Exception localException) {
  7. }
  8. }
  9. if (locale != null) {
  10. response.setLocale(locale);
  11. }
  12. if (this.paramsWorkaroundEnabled) {
  13. request.getParameter("foo");
  14. }
  15. request = wrapRequest(request); //封装 request请求
  16. serviceAction(request, response, getNameSpace(request), getActionName(request), getRequestMap(request), getParameterMap(request), getSessionMap(request), getApplicationMap());
  17. } catch (IOException e) {
  18. String message = "Could not wrap servlet request with MultipartRequestWrapper!";
  19. log.error(message, e);
  20. sendError(request, response, 500, new ServletException(message, e));
  21. }
  22. }

先来看看 wrapRequest 方法做了什么:

  1. protected HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
  2. if ((request instanceof MultiPartRequestWrapper)) {
  3. return request;
  4. }
  5. if (MultiPartRequest.isMultiPart(request)) {
  6. request = new MultiPartRequestWrapper(request, getSaveDir(), getMaxSize());
  7. }
  8. return request;
  9. }
  • 首先判断了传进来的 request 是不是已经被封装好的 MultiPartRequestWrapper,如果是就直接返回。
  • 否则再判断 request 的头信息里面的 Content­Type 是不是多类型参数 (multipart/form­data)的请求,如果是就把 request 包装成 WebWork 自己的 MultiPartRequestWrapper 类型,该类型继承HttpServletRequestWrapper

MultiPartRequest.isMultiPart() 方法实现如下:

  1. public static boolean isMultiPart(HttpServletRequest request){
  2. String content_type = request.getHeader("Content-Type");
  3. return content_type != null && content_type.startsWith("multipart/form-data");
  4. }
  • webwork.properties 里面配置文件的临时存储目录、还有最大上传大小,其实就是在这个时候起到作用的。
  • 创建 MultiPartRequestWrapper 对象的时候传入的参数是由 getSaveDir() 和 getMaxSize() 方 法 获 得 的 。
  • 在方法里会查找配置里面的 webwork.multipart.saveDir、 webwork.multipart.maxSize 属性,找到则使用该属性指定的临时目录和上传的最大内容,没找到就使用环境的临时目录。

具体实现如下:

  1. protected String getSaveDir() {
  2. String saveDir = Configuration.getString("webwork.multipart.saveDir").trim();
  3. if (saveDir.equals("")) {
  4. File tempdir = (File) getServletConfig().getServletContext().getAttribute("javax.servlet.context.tempdir");
  5. log.info("Unable to find 'webwork.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir");
  6. if (tempdir != null) {
  7. saveDir = tempdir.toString();
  8. }
  9. } else {
  10. File multipartSaveDir = new File(saveDir);
  11. if (!multipartSaveDir.exists()) {
  12. multipartSaveDir.mkdir();
  13. }
  14. }
  15. if (log.isDebugEnabled()) {
  16. log.debug("saveDir=" + saveDir);
  17. }
  18. return saveDir;
  19. }

2. 获取文件上传的解析类

再来看一下 MultiPartRequestWrapper 的构造函数使如何包装 request 的:

  1. public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize) throws IOException {
  2. super(request);
  3. if ((request instanceof MultiPartRequest)) {
  4. this.multi = ((MultiPartRequest) request);
  5. } else {
  6. String parser = "";
  7. parser = Configuration.getString("webwork.multipart.parser");
  8. if (parser.equals("")) {
  9. log.warn("Property webwork.multipart.parser not set. Using com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest");
  10. parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest";
  11. } else if (parser.equals("pell")) {
  12. parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest";
  13. } else if (parser.equals("cos")) {
  14. parser = "com.opensymphony.webwork.dispatcher.multipart.CosMultiPartRequest";
  15. } else if (parser.equals("jakarta")) {
  16. parser = "com.opensymphony.webwork.dispatcher.multipart.JakartaMultiPartRequest";
  17. }
  18. try {
  19. Class baseClazz = MultiPartRequest.class;
  20. Class clazz = Class.forName(parser);
  21. if (!baseClazz.isAssignableFrom(clazz)) {
  22. addError("Class '" + parser + "' does not extend MultiPartRequest");
  23. return;
  24. }
  25. Constructor ctor = clazz.getDeclaredConstructor(new Class[] { Class.forName("javax.servlet.http.HttpServletRequest"), String.class, Integer.TYPE });
  26. Object[] parms = { request, saveDir, new Integer(maxSize) };
  27. this.multi = ((MultiPartRequest) ctor.newInstance(parms));
  28. } catch (ClassNotFoundException e) {
  29. addError("Class: " + parser + " not found.");
  30. } catch (NoSuchMethodException e) {
  31. addError("Constructor error for " + parser + ": " + e);
  32. } catch (InstantiationException e) {
  33. addError("Error instantiating " + parser + ": " + e);
  34. } catch (IllegalAccessException e) {
  35. addError("Access errror for " + parser + ": " + e);
  36. } catch (InvocationTargetException e) {
  37. addError(e.getTargetException().toString());
  38. }
  39. }
  40. }
  • 首先它判断了传入的 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. 项目实战配置和使用

  • 配置文件
  1. action 配置:
  2. <action name="uploadAttach" class=".....attach.action.uploadAttach" caption="上传附件">
  3. <result name="success" type="dispatcher">
  4. <param name="location">/result.jsp</param>
  5. </result>
  6. <result name="error" type="dispatcher">
  7. <param name="location">/result.jsp</param>
  8. </result>
  9. <interceptor-ref name="defaultStack" />
  10. <interceptor-ref name="fileUploadStack" /> //webwok 上传所需要的拦截栈
  11. </action>
  12.  
  13. //拦截栈的定义
  14. <interceptor-stack name="fileUploadStack">
  15. <interceptor-ref name="fileUpload"/>
  16. <interceptor-ref name="params"/>
  17. </interceptor-stack>
  18.  
  19. //拦截栈对应的拦截器
  20. <interceptor name="fileUpload" class="com.opensymphony.webwork.interceptor.FileUploadInterceptor"/>
  21. <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
  1. public String intercept(ActionInvocation invocation) throws Exception {if (!(ServletActionContext.getRequest() instanceof MultiPartRequestWrapper)) {
  2. if (log.isDebugEnabled()) {
  3. log.debug("bypass " + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName());
  4. }
  5. return invocation.invoke();
  6. }
  7. Action action = invocation.getAction();
  8. ValidationAware validation = null;
  9. if ((action instanceof ValidationAware)) {
  10. validation = (ValidationAware) action;
  11. }
  12. MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) ServletActionContext.getRequest();
  13. if (multiWrapper.hasErrors()) {
  14. Collection errors = multiWrapper.getErrors();
  15. Iterator i = errors.iterator();
  16. while (i.hasNext()) {
  17. String error = (String) i.next();
  18. if (validation != null) {
  19. validation.addActionError(error);
  20. }
  21. log.error(error);
  22. }
  23. }
  24. Enumeration e = multiWrapper.getFileParameterNames();
  25. while ((e != null) && (e.hasMoreElements())) {
  26. String inputName = (String) e.nextElement();
  27.  
  28. String[] contentType = multiWrapper.getContentTypes(inputName);
  29.  
  30. String[] fileName = multiWrapper.getFileNames(inputName);
  31.  
  32. File[] file = multiWrapper.getFiles(inputName);
  33. if (file != null) {
  34. for (int i = 0; i < file.length; i++) {
  35. log.info("file " + inputName + " " + contentType[i] + " " + fileName[i] + " " + file[i]);
  36. }
  37. }
  38. if (file == null) {
  39. if (validation != null) {
  40. validation.addFieldError(inputName, "Could not upload file(s). Perhaps it is too large?");
  41. }
  42. log.error("Error uploading: " + fileName);
  43. } else {
  44. invocation.getInvocationContext().getParameters().put(inputName, file);
  45. invocation.getInvocationContext().getParameters().put(inputName + "ContentType", contentType);
  46. invocation.getInvocationContext().getParameters().put(inputName + "FileName", fileName);
  47. }
  48. }
  49. String result = invocation.invoke();
  50. for (Enumeration e1 = multiWrapper.getFileParameterNames(); e1 != null && e1.hasMoreElements();) {
  51. String inputValue = (String) e1.nextElement();
  52. File file[] = multiWrapper.getFiles(inputValue);
  53. for (int i = 0; i < file.length; i++) {
  54. File f = file[i];
  55. log.info("removing file " + inputValue + " " + f);
  56. if (f != null && f.isFile())
  57. f.delete();
  58. }
  59. }
  60. return result;
  61. }
  • 首先判断当前请求是否为 包含多媒体请求,如果是则记录日志,并执行 Action
  • 然后判断在文件上传过程 MultiPartRequestWrapper 是否含有错误,将错误信息,返回给客户端,不在继续调用 Action
  • 如果上面的判断条件都没有进行,开始遍历 MultiPartRequestWrapper  中的上传文件的参数,并将其中的文件名、文件内容类型放入Action 参数 map 中,供后面的业务类进行操作。
  • 在 WebWork 的 fileupload 拦截器功能中,它提供的 File只 是一个临时文件,Action 执行之后就会被自动删除,你必须在 Action中自己处理文件的存储问题,或者写到服务器的某个目录,或者保存到数据库中。如果你准备写到服务器的某个目录下面的话,你必须自己面临着处理文件同名的问题,但是实际上cos包已经提供了 文件重名的自动重命名规则。

Webwork 学习之路【07】文件上传下载的更多相关文章

  1. SpringMVC学习笔记八:文件上传下载(转)

    转自:http://www.cnblogs.com/WJ-163/p/6269409.html 一.关键步骤 ①引入核心JAR文件 SpringMVC实现文件上传,需要再添加两个jar包.一个是文件上 ...

  2. Spring Boot2(十四):单文件上传/下载,文件批量上传

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

  3. Webwork【07】文件上传下载

    Web上传和下载应该是很普遍的一个需求,无论是小型网站还是大并发访问的交易网站.WebWork 当然也提供了很友好的拦截器来实现对文件的上传,让我们可以专注与业务逻辑的设计和实现,在实现上传和下载时顺 ...

  4. Selenium2学习-039-WebUI自动化实战实例-文件上传下载

    通常在 WebUI 自动化测试过程中必然会涉及到文件上传的自动化测试需求,而开发在进行相应的技术实现是不同的,粗略可划分为两类:input标签类(类型为file)和非input标签类(例如:div.a ...

  5. Linux学习笔记(7)CRT实现windows与linux的文件上传下载

    Linux学习笔记(7)CRT实现windows与linux的文件上传下载 按下Alt + p 进入SFTP模式,或者右击选项卡进入 命令介绍 help 显示该FTP提供所有的命令 lcd 改变本地上 ...

  6. 【Java Web开发学习】Spring MVC文件上传

    [Java Web开发学习]Spring MVC文件上传 转载:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上传有两种实现方式,都比较 ...

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

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

  8. 艺萌文件上传下载及自动更新系统(基于networkComms开源TCP通信框架)

    1.艺萌文件上传下载及自动更新系统,基于Winform技术,采用CS架构,开发工具为vs2010,.net2.0版本(可以很容易升级为3.5和4.0版本)开发语言c#. 本系统主要帮助客户学习基于TC ...

  9. NetworkComms 文件上传下载和客户端自动升级(非开源)

    演示程序下载地址:http://pan.baidu.com/s/1geVfmcr 淘宝地址:https://shop183793329.taobao.com 联系QQ号:3201175853 许可:购 ...

随机推荐

  1. drop和delete的区别是什么

    当你不再需要该表时, 用 drop;当你仍要保留该表,但要删除所有记录时, 用 truncate;当你要删除部分记录时(always with a WHERE clause), 用 delete.

  2. Linux PHP5.3升级PHP5.5.33 (CentOS)

    由于要使用了laravel5.1,php要升级到5.5以上.具体环境是Aliyun Cent OS 7.0.由于阿里的yum源lastest只有5.4,laravel5.1必须php5.5,加了几个网 ...

  3. Linux 执行partprobe命令时遇到Unable to open /dev/sr0 read-write (Read-only file system)

    在使用fdisk创建分区时,我们会使用partprobe命令可以使kernel重新读取分区信息,从而避免重启系统,但是有时候会遇到下面错误信息"Warning: Unable to open ...

  4. java 生成和解析二维码

    public class QRCode { /** * 解析二维码(QRCode) * @param imgPath * @return */ public static String decoder ...

  5. Web环境使用相对路径发布Webservice

    常我们的Webservice服务的发布地址都将是一个相对路径,在与Spring一起使用时我们需要引入Cxf配置Webservice的schema,如jaxws,用以定义对应的Webservice. & ...

  6. 中科院分词ICTCLAS5.0_JNI 使用方法

    1.简介 中国科学院计算技术研究所在多年研究基础上,耗时一年研制出了基于多层隐码模型的汉语词法分析系统 ICTCLAS(Institute of Computing Technology, Chine ...

  7. CentOS最大文件描述符限制更改

    系统级的限制:/proc/sys/fs/file-max中设定了系统最大能打开的文件数. 查看该值可以用如下方式: [root@#panda ~]# cat /proc/sys/fs/file-max ...

  8. jquery——彩色投票进度条

    一.需求 如下图 重点是要实现进度条. 二.分析 html5新增及删除标签一文中提到过html5新增了progress标签.但是肯定有兼容性问题.生成环境不适用,所以要模拟实现. 原理:动态设置< ...

  9. 扫描线+堆 codevs 2995 楼房

    2995 楼房  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 黄金 Gold 题解       题目描述 Description 地平线(x轴)上有n个矩(lou)形(fan ...

  10. TortoiseSVN status cache占用CPU高

    进程占用CPU高 每次从SVN上更新资源时,电脑都会卡死,直到资源更新完.当要Commit资源时,SVN也会卡死资源管理器,如下图所示: 解决占用CPU高的问题 1.禁用图标缓存 2.排除路径和包含路 ...