• Webwork【07】文件上传下载


          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"/>
    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包已经提供了 文件重名的自动重命名规则。 
  • 相关阅读:
    IIS7开启目录浏览功能
    IIS7文件无法下载问题处理
    Dubbo端口占用错误信息
    PlSqlDev中执行INSERT SQL语句包含&符号导致数据异常
    VMWare版本兼容问题处理
    手动释放Linux内存
    ORA-00972_标识符过长
    通过API获取统计信息时报Access denied错误处理记录
    微信小程序中跳转另一个小程序
    css怎样去掉多个Img标签之间的间隙
  • 原文地址:https://www.cnblogs.com/java-class/p/5175619.html
Copyright © 2020-2023  润新知