Webwork 实现文件上传下载代码详解


Posted in Javascript onFebruary 02, 2016

本文主要从三个方面给大家介绍webwork文件上传下载知识,包括以下三个方面:

1. 包装 Request 请求
2. 获取文件上传的解析类
3. 项目实战配置和使用

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

Webwork 实现文件上传下载代码详解

1. 包装 Request 请求

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

具体过程请参照:详解Webwork中Action 调用的方法

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参数名。如果直接使用 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这里就不多说了,有官方网址:jQuery AjaxUpload 上传图片代码

•通过对 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 实现文件上传下载的相关知识,希望对大家有所帮助。

Javascript 相关文章推荐
参考:关于Javascript中实现暂停的几篇文章
Mar 04 Javascript
JavaScript 中的replace方法说明
Apr 13 Javascript
Jquery操作Select 简单方便 一个js插件搞定
Nov 12 Javascript
javascript倒计时功能实现代码
Jun 07 Javascript
微信小程序 location API实例详解
Oct 02 Javascript
bootstrap中添加额外的图标实例代码
Feb 15 Javascript
JS控件bootstrap datepicker使用方法详解
Mar 25 Javascript
vue监听滚动事件实现滚动监听
Apr 11 Javascript
JS实现留言板功能
Jun 17 Javascript
jquery自定义显示消息数量
Dec 19 jQuery
JavaScript实现身份证验证代码实例
Aug 26 Javascript
js实现纯前端压缩图片
Nov 16 Javascript
javascript自动切换焦点控制效果完整实例
Feb 02 #Javascript
原生js实现图片层叠轮播切换效果
Feb 02 #Javascript
Javascript实现的SHA-256加密算法完整实例
Feb 02 #Javascript
JavaScript实现的SHA-1加密算法完整实例
Feb 02 #Javascript
javascript实现瀑布流加载图片原理
Feb 02 #Javascript
基于JQuery实现图片轮播效果(焦点图)
Feb 02 #Javascript
Hammer.js+轮播原理实现简洁的滑屏功能
Feb 02 #Javascript
You might like
php使用正则表达式提取字符串中尖括号、小括号、中括号、大括号中的字符串
2020/04/05 PHP
学习php设计模式 php实现模板方法模式
2015/12/08 PHP
php中mkdir()函数的权限问题分析
2016/09/24 PHP
PHP中SERIALIZE和JSON的序列化与反序列化操作区别分析
2016/10/11 PHP
PHP学习笔记之session
2018/05/06 PHP
jquery keypress,keyup,onpropertychange键盘事件
2010/06/25 Javascript
基于jquery的使ListNav兼容中文首字拼音排序的实现代码
2011/07/10 Javascript
js 窗口抖动示例
2013/09/04 Javascript
Jquery 过滤器(first,last,not,even,odd)的使用
2014/01/22 Javascript
js中把JSON字符串转换成JSON对象最好的方法
2014/03/21 Javascript
JS实现的在线调色板实例(附demo源码下载)
2016/03/01 Javascript
Vue.js使用v-show和v-if的注意事项
2016/12/13 Javascript
JS实现鼠标移上去显示图片或微信二维码
2016/12/14 Javascript
Vue2递归组件实现树形菜单
2017/04/10 Javascript
Node.js实现连接mysql数据库功能示例
2017/09/15 Javascript
angular指令笔记ng-options的使用方法
2017/09/18 Javascript
详解基于 Nuxt 的 Vue.js 服务端渲染实践
2017/10/24 Javascript
js经验分享 JavaScript反调试技巧
2018/03/10 Javascript
[22:59]VGJ.S vs VG 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
详解Django框架中用户的登录和退出的实现
2015/07/23 Python
virtualenv实现多个版本Python共存
2017/08/21 Python
python生成随机图形验证码详解
2017/11/08 Python
TensorFlow实现简单卷积神经网络
2018/05/24 Python
Django 外键的使用方法详解
2019/07/19 Python
Pytorch实验常用代码段汇总
2020/11/19 Python
基于HTML5+tracking.js实现刷脸支付功能
2020/04/16 HTML / CSS
凯撒娱乐:Caesars Entertainment
2018/02/23 全球购物
Orvis官网:自1856年以来,优质服装、飞钓装备等
2018/12/17 全球购物
python+selenium小米商城红米K40手机自动抢购的示例代码
2021/03/24 Python
二年级小学生评语
2014/04/21 职场文书
电子专业毕业生自荐信
2014/05/25 职场文书
教师远程培训心得体会
2016/01/09 职场文书
大学生村官驻村工作心得体会
2016/01/23 职场文书
浅析MySQL如何实现事务隔离
2021/06/26 MySQL
Mysql InnoDB 的内存逻辑架构
2022/05/06 MySQL
python内置模块之上下文管理contextlib
2022/06/14 Python