struts2的文件上传操作源码解析
2016-09-25 18:08
519 查看
首先来看一下struts2的过滤器的doFilter方法
看到这一行代码 request = prepare.wrapRequest(request);跟进去看一下。
继续看这个dispatcher.wrapRequest(request, servletContext);方法
如果当前的request是strutsRequestWrapper实例则直接return,如果不是继续执行,如果是文件上传类型,那么走下面的代码,否则创建StrutsRequestWrapper对象返回;
看到这一行代码MultiPartRequest mpr = getMultiPartRequest();查看一下getMultiPartRequest()方法
这里就是创建了一个MultiPartRequest对象,没有收获,看看wrapRequest()方法里的其他代码
看到这么一条代码request = new MultiPartRequestWrapper(mpr, request, getSaveDir(servletContext), provider);
这里有收获,看看multi.parse()方法,如果这个方法出错将会将错误放入到actionError中,可以在jsp页面通过
这里看到 processUpload(request, saveDir);这个方法,处理了文件上传
这里看item.isFromField(),如果是表单的普通字段则使用processNormalFormField(item, request.getCharacterEncoding());如果是file类型的字段,则用 processFileField(item);
下面我们就来看一下这两个方法。
这里如果是普通字段将加入到MultiPartRequest对象的params中。
如果是文件字段,则加入到MultiPartRequest对象的files中。
下面开始就到文件上传的拦截器咯!!!
直接来看一下intercept方法
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”,三个字段。
这里文件上传拦截器到此结束,下面是参数的拦截器。
这里看这两条代码ValueStack stack = ac.getValueStack(); setParameters(action, stack, parameters);
首先这个parameters是actioncontext中的Parameters,那么我们来看看setParameters()这个方法。
这里newStack.setParameter(name, value);这一条,看看,name对应的parameter的key,value对应的parameter的value。查看valuestack的setParameter()方法
调用了内部的setValue方法。
前面我们知道setValue方法
setValue(expr,value)像表达式所对应的栈或者contextMap中添加数据,如果表达式不以#开头,会在栈中寻找setExpr的方法,setValue 方法必须要求有该属性的setter方法,否则会报错.如果以#开头,则直接存放contextMap中。
所以设置参数的值会首先在ValueStack中寻找,我们知道创建action时,将action放入了根栈中,所以在action必须要要有参数的字段并且有set方法
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 s 4000 aveDir) 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方法
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- 自动共享和上传文件到兼容的托管站点
- JQuery+Strusts1.x无刷新登录
- 在 Linux 上创建文件的 10 个方法
- 如何从命令行同时移动多种文件类型
- acl权限列表
- 命令行小技巧:读取文件的不同方式
- 实现FTP整站上传的批处理代码
- IE:临时文件保存法
- 文件的读出 编辑 管理
- 文件遍历排序函数
- 在线用表单建立文件夹
- VB获取文件大小的方法
- 文件、目录,文本文件等多种操作类
- 处理驱动器和文件夹
- asp防止上传图片木马原理解析
- 用vbs删除某些类型文件和磁盘空间报告的脚本
- 批处理向FTP上传具有指定属性的文件(增量备份)
- Ruby实现批量对文件增加前缀代码分享
- C#获取文件夹及文件的大小与占用空间的方法