白话MVC(四)为Struts2编写文件上传插件

Struts2中,在Dispatcher.java里,把一个multipart/form-data类型的 HttpServletRequest对象包装成JakartaMultiPartRequest的过程中,已经读完了HttpServletRequest对象输入流数据,基于此对象输入流的特点--不可再读取第二次,因此Action中已经不可能使用fastupload API解析multipart/form-data请求了。

纵览struts2框架里Model Bean的自动装配过程,发现拦截器其实共享了一个ActionContext的“值栈”,“值栈”里存储了必要的数据,以便让拦截器、ActionProxy、ActionInvocation对象和HttpServletRequest隔离,最后在ParametersInterceptor.java中,从“值栈”取得相关的数据,装配到Model Bean中,因此,任何数据在装配之前,都需要在合适的时机放入“值栈”之中。Struts2虽然在包装HttpServletRequest阶段已经完成了文件解析的工作,但是直到FileUploadInterceptor被调用时,把解析的文件放入了“值栈”。

fastupload基于这个约定,编写了struts2文件上传的“插件”,之所以打上引号,其功能实质上算不了一个正真的插件,只是实现了MultiPartRequest这个接口过程中调用了fastupload相关的API。具体的用法请参考《Fastupload 0.4.7发布,支持struts2》一文。这种解决办法只能说是凑合,好处是---- 不需要修改struts2的源码。

但是struts2在使用apache commons fileupload的过程中,写的MultiPartRequestWrapper.java的构造函数是多了一个saveDir,用来指定apache commons fileupload解析时产生临时文件的存储目录,并且在ActionProxy对象执行结束后,要清除这些临时文件,不止于此,在Action中,用来操作上传的文件的类用了java.io.File,也就是一定要和文件系统相关联。不能不说, apache commons fileupload使用临时文件这种方式,拖累了struts2,spring mvc以及其他一些mvc框架。

假如解析multipart/form-data请求后,这些解析的数据都作为内存中的一个对象存在,在需要的时候才保存到文件系统中,mvc框架处理起来要简单的多,是不是这样?正因如此,要发挥fastupload的威力,一定要用内存的解析方式!

首先看一下使用内存解析方式,Action类有何变化!

public class StrutUploadAction1 extends ActionSupport {

	private MultiPartFile photo;

        private String description;

        @Override
	public String execute() throws Exception {
		System.out.println(photo); //此处省略业务逻辑代码
		return super.execute();
	}
//省略getters & setters
}
	 

 这个Actin中直接使用MultPartFile,解析后的上传文件的数据也保存在这个对象之中,也就是,上传文件的数据已经在内存之中 用户很方便的把这些数据保存到文件系统还是拿到数据,处理完后直接丢弃。这种处理方式,给予用户极大的便利。

但是,struts2对于multipart/form-data请求的解析过程中,借用了临时文件。对用户而言, 不仅 处理上传文件不方便,而且还造成WEB APP性能低下,还要在Action被代理执行后清除这些临时文件。


struts2沿用这种处理方式已久, multipart/form-data请求的处理,处处体现了这个特征。fastupload针对这种现象,作出一个小规模的源码侵入。实现的步骤如下:

1, 修改struts配置,使用FastUploadInterceptor,配置其参数,注意这个fileUploadAction配置 。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
	<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest" name="fastupload" 
		class="org.apache.struts2.dispatcher.multipart.FastUploadMultiPartRequest" scope="default" />
    <constant name="struts.multipart.handler" value="fastupload" />
    <constant name="fastupload.parse.type" value="auto"/>
    
     <constant name="fastupload.allowed.types" value="image/jpeg, image/png, image/gif"/>
     <constant name="fastupload.allowed.extensions" value=".jpeg, .png, .gif"/>
    
	<package name="example" namespace="/example" extends="struts-default">
		
		<interceptors>
			<interceptor name="fastuploadInterceptor" class="org.apache.struts2.dispatcher.multipart.FastUploadInterceptor" />
		</interceptors>
		
		<action name="fileUploadAction" class="example.FileUpload">
			<interceptor-ref name="fastuploadInterceptor"/>
			<interceptor-ref name="basicStack"/>
			<result>/example/upload.jsp</result>
		</action>

	</package>
</struts>
 

2, 修改Dispatcher.warpRequest(HttpServletRequest, ServletContext)函数,用FastUploadMultiPartRequestWrapper替换struts2原有的warpper。

//TODO un-comment the next line for original code
            // request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext));
            request = new FastUploadMultiPartRequestWrapper(mpr, request, getSaveDir(servletContext)); 
 

3,既然使用了内存解析方式,不需要临时文件,也不用清理它们,注释掉Dispathcer.cleanUpRequest(HttpServletRequest)函数中所有的代码

    /**
     * Removes all the files created by MultiPartRequestWrapper.
     *
     * @param request the HttpServletRequest object.
     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
     * @throws java.io.IOException on any error.
     */
    public void cleanUpRequest(HttpServletRequest request) throws IOException {
//        if (!(request instanceof MultiPartRequestWrapper)) {
//            return;
//        }
//
//        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
//
//        Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
//        while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
//            String inputValue = (String) fileParameterNames.nextElement();
//            File[] files = multiWrapper.getFiles(inputValue);
//
//            for (File currentFile : files) {
//                if (LOG.isInfoEnabled()) {
//                    String msg = LocalizedTextUtil.findText(this.getClass(), "struts.messages.removing.file", Locale.ENGLISH, "no.message.found", new Object[]{inputValue, currentFile});
//                    LOG.info(msg);
//                }
//
//                if ((currentFile != null) && currentFile.isFile()) {
//                    if (!currentFile.delete()) {
//                        if (LOG.isWarnEnabled()) {
//                            LOG.warn("Resource Leaking:  Could not remove uploaded file '" + currentFile.getCanonicalPath() + "'.");
//                        }
//                    }
//                }
//            }
//        }
    }

4, 使用fastupload预先的过滤机制(可选),在FastUploadMultiPartRequest类中,找到下面的代码,把@Inject那两行反注释掉

/**
	 * not exposed yet
	 * 
	 * @param allowedTypes
	 *            the allowedTypes to set
	 */
	// @Inject("fastupload.allowed.types")
	public void setAllowedTypes(String allowedTypes) {
		this.allowedTypes = allowedTypes;
	}

	/**
	 * not exposed yet
	 * 
	 * @param allowedExtensions
	 *            the allowedExtensions to set
	 */
	// @Inject("fastupload.allowed.extensions")
	public void setAllowedExtensions(String allowedExtensions) {
		this.allowedExtensions = allowedExtensions;
	}

 向struts配置文件中增加两个全局配置常量,这用过滤设置目前还不支持通配符,详细的用法请参考fastupload项目中的 AcceptableFileFactory.java

 <constant name="fastupload.allowed.types" value="image/jpeg, image/png, image/gif"/>
  <constant name="fastupload.allowed.extensions" value=".jpeg, .png, .gif"/>
 

5 运行并测试那个Action -:)


采用侵入源码的方式,也是逼迫不已呀。就目前来说,也达到让fastupload支持struts2的目标,但是,如果能让struts2的发行包中,直接集成了fastupload组建高效解析API,才能让struts2框架使用者更容易的使用,需要与struts的开发小组讨论了... ... 这个阶段 结束,一个新的阶段接着开始。

fastupload支持struts2框架的开发过程比较仓促,加之本人水平有限,可能会有一些疏漏及不正确的地方,如果您发现了,请告诉我,我及时改正,同时也欢迎网友对fastupload提出建设性意见。

[原创文章,如转载,请注明出去!@仪山湖]

猜你喜欢

转载自mojarra.iteye.com/blog/1706925