struts2的文件上传操作源码解析

来源:互联网 发布:360里有个网络监控 编辑:程序博客网 时间:2024/06/05 17:09

首先来看一下struts2的过滤器的doFilter方法

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        try {            prepare.setEncodingAndLocale(request, response);            prepare.createActionContext(request, response);            prepare.assignDispatcherToThread();            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {                chain.doFilter(request, response);            } else {                request = prepare.wrapRequest(request);                ActionMapping mapping = prepare.findActionMapping(request, response, true);                if (mapping == null) {                    boolean handled = execute.executeStaticResourceRequest(request, response);                    if (!handled) {                        chain.doFilter(request, response);                    }                } else {                    execute.executeAction(request, response, mapping);                }            }        } finally {            prepare.cleanupRequest(request);        }    }

看到这一行代码 request = prepare.wrapRequest(request);跟进去看一下。

 public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {        HttpServletRequest request = oldRequest;        try {            // Wrap request first, just in case it is multipart/form-data            // parameters might not be accessible through before encoding (ww-1278)            request = dispatcher.wrapRequest(request, servletContext);        } catch (IOException e) {            throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);        }        return request;    }

继续看这个dispatcher.wrapRequest(request, servletContext);方法

public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) throws IOException {        // don't wrap more than once        if (request instanceof StrutsRequestWrapper) {            return request;        }        String content_type = request.getContentType();        if (content_type != null && content_type.contains("multipart/form-data")) {            MultiPartRequest mpr = getMultiPartRequest();            LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);            request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext), provider);        } else {            request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);        }        return request;    }

如果当前的request是strutsRequestWrapper实例则直接return,如果不是继续执行,如果是文件上传类型,那么走下面的代码,否则创建StrutsRequestWrapper对象返回;
看到这一行代码MultiPartRequest mpr = getMultiPartRequest();查看一下getMultiPartRequest()方法

 protected MultiPartRequest getMultiPartRequest() {        MultiPartRequest mpr = null;        //check for alternate implementations of MultiPartRequest        Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);        for (String multiName : multiNames) {            if (multiName.equals(multipartHandlerName)) {                mpr = getContainer().getInstance(MultiPartRequest.class, multiName);            }        }        if (mpr == null ) {            mpr = getContainer().getInstance(MultiPartRequest.class);        }        return mpr;    }

这里就是创建了一个MultiPartRequest对象,没有收获,看看wrapRequest()方法里的其他代码
看到这么一条代码request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext), provider);

 public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir, LocaleProvider provider) {        super(request);        errors = new ArrayList<String>();        multi = multiPartRequest;        defaultLocale = provider.getLocale();        setLocale(request);        try {            multi.parse(request, saveDir);            for (String error : multi.getErrors()) {                addError(error);            }        } catch (IOException e) {            if (LOG.isWarnEnabled()) {                LOG.warn(e.getMessage(), e);            }            addError(buildErrorMessage(e, new Object[] {e.getMessage()}));        }     }

这里有收获,看看multi.parse()方法,如果这个方法出错将会将错误放入到actionError中,可以在jsp页面通过

public void parse(HttpServletRequest request, String saveDir) throws IOException {        try {            setLocale(request);            processUpload(request, saveDir);        } catch (FileUploadBase.SizeLimitExceededException e) {            if (LOG.isWarnEnabled()) {                LOG.warn("Request exceeded size limit!", e);            }            String errorMessage = buildErrorMessage(e, new Object[]{e.getPermittedSize(), e.getActualSize()});            if (!errors.contains(errorMessage)) {                errors.add(errorMessage);            }        } catch (Exception e) {            if (LOG.isWarnEnabled()) {                LOG.warn("Unable to parse request", e);            }            String errorMessage = buildErrorMessage(e, new Object[]{});            if (!errors.contains(errorMessage)) {                errors.add(errorMessage);            }        }    }

这里看到 processUpload(request, saveDir);这个方法,处理了文件上传

 private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException {        for (FileItem item : parseRequest(request, saveDir)) {            if (LOG.isDebugEnabled()) {                LOG.debug("Found item " + item.getFieldName());            }            if (item.isFormField()) {                processNormalFormField(item, request.getCharacterEncoding());            } else {                processFileField(item);            }        }    }

这里看item.isFromField(),如果是表单的普通字段则使用processNormalFormField(item, request.getCharacterEncoding());如果是file类型的字段,则用 processFileField(item);
下面我们就来看一下这两个方法。

private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException {        if (LOG.isDebugEnabled()) {            LOG.debug("Item is a normal form field");        }        List<String> values;        if (params.get(item.getFieldName()) != null) {            values = params.get(item.getFieldName());        } else {            values = new ArrayList<String>();        }        // note: see http://jira.opensymphony.com/browse/WW-633        // basically, in some cases the charset may be null, so        // we're just going to try to "other" method (no idea if this        // will work)        if (charset != null) {            values.add(item.getString(charset));        } else {            values.add(item.getString());        }        params.put(item.getFieldName(), values);        item.delete();    }

这里如果是普通字段将加入到MultiPartRequest对象的params中。

   private void processFileField(FileItem item) {        if (LOG.isDebugEnabled()) {            LOG.debug("Item is a file upload");        }        // Skip file uploads that don't have a file name - meaning that no file was selected.        if (item.getName() == null || item.getName().trim().length() < 1) {            LOG.debug("No file has been uploaded for the field: " + item.getFieldName());            return;        }        List<FileItem> values;        if (files.get(item.getFieldName()) != null) {            values = files.get(item.getFieldName());        } else {            values = new ArrayList<FileItem>();        }        values.add(item);        files.put(item.getFieldName(), values);    }

如果是文件字段,则加入到MultiPartRequest对象的files中。

下面开始就到文件上传的拦截器咯!!!

<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>

直接来看一下intercept方法

 public String intercept(ActionInvocation invocation) throws Exception {        ActionContext ac = invocation.getInvocationContext();        HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);        if (!(request instanceof MultiPartRequestWrapper)) {            if (LOG.isDebugEnabled()) {                ActionProxy proxy = invocation.getProxy();                LOG.debug(getTextMessage("struts.messages.bypass.request", new String[]{proxy.getNamespace(), proxy.getActionName()}));            }            return invocation.invoke();        }        ValidationAware validation = null;        Object action = invocation.getAction();        if (action instanceof ValidationAware) {            validation = (ValidationAware) action;        }        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;        if (multiWrapper.hasErrors()) {            for (String error : multiWrapper.getErrors()) {                if (validation != null) {                    validation.addActionError(error);                }            }        }        // bind allowed Files        Enumeration fileParameterNames = multiWrapper.getFileParameterNames();        while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {            // get the value of this input tag            String inputName = (String) fileParameterNames.nextElement();            // get the content type            String[] contentType = multiWrapper.getContentTypes(inputName);            if (isNonEmpty(contentType)) {                // get the name of the file from the input tag                String[] fileName = multiWrapper.getFileNames(inputName);                if (isNonEmpty(fileName)) {                    // get a File object for the uploaded File                    File[] files = multiWrapper.getFiles(inputName);                    if (files != null && files.length > 0) {                        List<File> acceptedFiles = new ArrayList<File>(files.length);                        List<String> acceptedContentTypes = new ArrayList<String>(files.length);                        List<String> acceptedFileNames = new ArrayList<String>(files.length);                        String contentTypeName = inputName + "ContentType";                        String fileNameName = inputName + "FileName";                        for (int index = 0; index < files.length; index++) {                            if (acceptFile(action, files[index], fileName[index], contentType[index], inputName, validation)) {                                acceptedFiles.add(files[index]);                                acceptedContentTypes.add(contentType[index]);                                acceptedFileNames.add(fileName[index]);                            }                        }                        if (!acceptedFiles.isEmpty()) {                            Map<String, Object> params = ac.getParameters();                            params.put(inputName, acceptedFiles.toArray(new File[acceptedFiles.size()]));                            params.put(contentTypeName, acceptedContentTypes.toArray(new String[acceptedContentTypes.size()]));                            params.put(fileNameName, acceptedFileNames.toArray(new String[acceptedFileNames.size()]));                        }                    }                } else {                    if (LOG.isWarnEnabled()) {                        LOG.warn(getTextMessage(action, "struts.messages.invalid.file", new String[]{inputName}));                    }                }            } else {                if (LOG.isWarnEnabled()) {                    LOG.warn(getTextMessage(action, "struts.messages.invalid.content.type", new String[]{inputName}));                }            }        }        // invoke action        return invocation.invoke();    }

MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;这一条代码将request对象转化为MultiPartRequestWrapper ,从而就可以从中获取files和params,而且MultiPartRequest和JakartaMultiPartRequest是父子关系。MultiPartRequestWrapper 这个里面成员变量有MultiPartRequest对象。
下面是通过表单中文件字段名获取文件名,文件格式,最后是将文件字段名,文件格式,文件名放入到actioncontext对象的Parameters中。
String contentTypeName = inputName + “ContentType”;
String fileNameName = inputName + “FileName”;
最后是inputName,contentTypeName,fileNameName以key放入params,从而可以知道文件上传的action中必须要有文件类型字段inputName,String类型inputName+”ContentType”,String类型的inputName+”FileName”,三个字段。

这里文件上传拦截器到此结束,下面是参数的拦截器。

 <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
@Override    public String doIntercept(ActionInvocation invocation) throws Exception {        Object action = invocation.getAction();        if (!(action instanceof NoParameters)) {            ActionContext ac = invocation.getInvocationContext();            final Map<String, Object> parameters = retrieveParameters(ac);            if (LOG.isDebugEnabled()) {                LOG.debug("Setting params {}", getParameterLogMap(parameters));            }            if (parameters != null) {                Map<String, Object> contextMap = ac.getContextMap();                try {                    ReflectionContextState.setCreatingNullObjects(contextMap, true);                    ReflectionContextState.setDenyMethodExecution(contextMap, true);                    ReflectionContextState.setReportingConversionErrors(contextMap, true);                    ValueStack stack = ac.getValueStack();                    setParameters(action, stack, parameters);                } finally {                    ReflectionContextState.setCreatingNullObjects(contextMap, false);                    ReflectionContextState.setDenyMethodExecution(contextMap, false);                    ReflectionContextState.setReportingConversionErrors(contextMap, false);                }            }        }        return invocation.invoke();    }

这里看这两条代码ValueStack stack = ac.getValueStack(); setParameters(action, stack, parameters);
首先这个parameters是actioncontext中的Parameters,那么我们来看看setParameters()这个方法。

protected void setParameters(final Object action, ValueStack stack, final Map<String, Object> parameters) {        Map<String, Object> params;        Map<String, Object> acceptableParameters;        if (ordered) {            params = new TreeMap<>(getOrderedComparator());            acceptableParameters = new TreeMap<>(getOrderedComparator());            params.putAll(parameters);        } else {            params = new TreeMap<>(parameters);            acceptableParameters = new TreeMap<>();        }        for (Map.Entry<String, Object> entry : params.entrySet()) {            String name = entry.getKey();            Object value = entry.getValue();            if (isAcceptableParameter(name, action)) {                acceptableParameters.put(name, entry.getValue());            }        }        ValueStack newStack = valueStackFactory.createValueStack(stack);        boolean clearableStack = newStack instanceof ClearableValueStack;        if (clearableStack) {            //if the stack's context can be cleared, do that to prevent OGNL            //from having access to objects in the stack, see XW-641            ((ClearableValueStack)newStack).clearContextValues();            Map<String, Object> context = newStack.getContext();            ReflectionContextState.setCreatingNullObjects(context, true);            ReflectionContextState.setDenyMethodExecution(context, true);            ReflectionContextState.setReportingConversionErrors(context, true);            //keep locale from original context            context.put(ActionContext.LOCALE, stack.getContext().get(ActionContext.LOCALE));        }        boolean memberAccessStack = newStack instanceof MemberAccessValueStack;        if (memberAccessStack) {            //block or allow access to properties            //see WW-2761 for more details            MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;            accessValueStack.setAcceptProperties(acceptedPatterns.getAcceptedPatterns());            accessValueStack.setExcludeProperties(excludedPatterns.getExcludedPatterns());        }        for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {            String name = entry.getKey();            Object value = entry.getValue();            try {                newStack.setParameter(name, value);            } catch (RuntimeException e) {                if (devMode) {                    notifyDeveloperParameterException(action, name, e.getMessage());                }            }        }        if (clearableStack && (stack.getContext() != null) && (newStack.getContext() != null))            stack.getContext().put(ActionContext.CONVERSION_ERRORS, newStack.getContext().get(ActionContext.CONVERSION_ERRORS));        addParametersToContext(ActionContext.getContext(), acceptableParameters);    }

这里newStack.setParameter(name, value);这一条,看看,name对应的parameter的key,value对应的parameter的value。查看valuestack的setParameter()方法

 public void setParameter(String expr, Object value) {        setValue(expr, value, devMode);    }

调用了内部的setValue方法。
前面我们知道setValue方法
setValue(expr,value)像表达式所对应的栈或者contextMap中添加数据,如果表达式不以#开头,会在栈中寻找setExpr的方法,setValue 方法必须要求有该属性的setter方法,否则会报错.如果以#开头,则直接存放contextMap中。
所以设置参数的值会首先在ValueStack中寻找,我们知道创建action时,将action放入了根栈中,所以在action必须要要有参数的字段并且有set方法

0 0
原创粉丝点击