本文主要从三个方面给大家介绍webwork文件上传下载知识,包括以下三个方面:
1. 包装 request 请求
2. 获取文件上传的解析类
3. 项目实战配置和使用
web上传和下载应该是很普遍的一个需求,无论是小型网站还是大并发访问的交易网站。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 的头信息里面的 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 配置:
/result.jsp/result.jsp //webwok 上传所需要的拦截栈//拦截栈的定义 //拦截栈对应的拦截器
•前端使用比较稳定、功能比较强大的 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 实现文件上传下载的相关知识,希望对大家有所帮助。