您的位置:首页 > 编程语言 > Java开发

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

2016-09-25 18:08 519 查看
首先来看一下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 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方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  struts 源码 文件 上传