// 根据 已有的一些信息和修复版本的代码,推测应该是如下的触发流程
// 因为没有测试环境,也只是不严谨的代码触发流程推测,不保证正确性,欢迎大神交流分享。
//core\src\main\java\org\apache\struts2\dispatcher\multipart\JakartaMultiPartRequest.java
public void parse(HttpServletRequest request, String saveDir) throws IOException
{
try
{
setLocale(request);
processUpload(request, saveDir); // 调用 1
}
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) // 调用 7 捕获异常
{
if (LOG.isWarnEnabled())
{
LOG.warn("Unable to parse request", e);
}
String errorMessage = buildErrorMessage(e, new Object[] {}); //调用 8
if (!errors.contains(errorMessage))
{
errors.add(errorMessage);
}
}
}
protected String buildErrorMessage(Throwable e, Object[] args)
{
String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName();
if (LOG.isDebugEnabled())
{
LOG.debug("Preparing error message for key: [#0]", errorKey);
}
return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args);
// 调用9,触发
// 因为 args 为空,使用默认的 e.getMessage() 为 "the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is" + content-type
// If a message is found, it will also be interpolated. Anything within ${...} will be treated as an OGNL expression and evaluated as such.
// 故执行了http 请求头content-type 中的{}内部引入的指令
// reference:https://struts.apache.org/maven/struts2-core/apidocs/com/opensymphony/xwork2/util/LocalizedTextUtil.html#findText(java.lang.Class, java.lang.String, java.util.Locale, java.lang.String, java.lang.Object[])
}
protected void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException
{
for (FileItem item : parseRequest(request, saveDir)) // 调用2,如下所示
{
if (LOG.isDebugEnabled())
{
LOG.debug("Found item " + item.getFieldName());
}
if (item.isFormField())
{
processNormalFormField(item, request.getCharacterEncoding());
}
else
{
processFileField(item);
}
}
}
protected List parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException
{
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
ServletFileUpload upload = createServletFileUpload(fac);
return upload.parseRequest(createRequestContext(servletRequest)); // 调用 2.1
}
protected ServletFileUpload createServletFileUpload(DiskFileItemFactory fac)
{
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
return upload;
}
// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload.servlet ServletFileUpload.java
public class ServletFileUpload extends FileUpload
{
public static final boolean isMultipartContent(final HttpServletRequest request)
{
if (!"post".equals(request.getMethod().toLowerCase()))
{
return false;
}
final String contentType = request.getContentType();
return contentType != null && contentType.toLowerCase().startsWith("multipart/");
}
public ServletFileUpload()
{
}
public ServletFileUpload(final FileItemFactory fileItemFactory)
{
super(fileItemFactory);
}
public List parseRequest(final HttpServletRequest request) throws FileUploadException
{
return this.parseRequest(new ServletRequestContext(request)); // 调用 2.2
}
public FileItemIterator getItemIterator(final HttpServletRequest request) throws FileUploadException, IOException
{
return super.getItemIterator(new ServletRequestContext(request));
}
}
// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload FileUploadBase.java
public List parseRequest(final RequestContext ctx) throws FileUploadException
{
try
{
final FileItemIterator iter = this.getItemIterator(ctx); // 调用 3
final List items = new ArrayList();
final FileItemFactory fac = this.getFileItemFactory();
if (fac == null)
{
throw new NullPointerException("No FileItemFactory has been set.");
}
while (iter.hasNext())
{
final FileItemStream item = iter.next();
final FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), item.getName());
try
{
Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
}
catch (FileUploadIOException e)
{
throw (FileUploadException)e.getCause();
}
catch (IOException e2)
{
throw new IOFileUploadException("Processing of multipart/form-data request failed. " + e2.getMessage(), e2);
}
if (fileItem instanceof FileItemHeadersSupport)
{
final FileItemHeaders fih = item.getHeaders();
((FileItemHeadersSupport)fileItem).setHeaders(fih);
}
items.add(fileItem);
}
return items;
}
catch (FileUploadIOException e3)
{
throw (FileUploadException)e3.getCause();
}
catch (IOException e4)
{
throw new FileUploadException(e4.getMessage(), e4);
}
}
public FileItemIterator getItemIterator(final RequestContext ctx) throws FileUploadException, IOException
{
return new FileItemIteratorImpl(ctx); //调用 4
}
private class FileItemIteratorImpl implements FileItemIterator
{
private final MultipartStream multi;
private final MultipartStream.ProgressNotifier notifier;
private final byte[] boundary;
private FileItemStreamImpl currentItem;
private String currentFieldName;
private boolean skipPreamble;
private boolean itemValid;
private boolean eof;
private final /* synthetic */ FileUploadBase this$0;
// 构造函数调用 5
FileItemIteratorImpl(final RequestContext ctx) throws FileUploadException, IOException
{
if (ctx == null)
{
throw new NullPointerException("ctx parameter");
}
final String contentType = ctx.getContentType();
if (null == contentType || !contentType.toLowerCase().startsWith("multipart/"))
{
throw new InvalidContentTypeException("the request doesn't contain a multipart/form-data or multipart/mixed stream, content type header is " + contentType);
// 调用6,抛出异常
}
InputStream input = ctx.getInputStream();
if (FileUploadBase.this.sizeMax >= 0L)
{
final int requestSize = ctx.getContentLength();
if (requestSize == -1)
{
input = new LimitedInputStream(input, FileUploadBase.this.sizeMax)
{
protected void raiseError(final long pSizeMax, final long pCount) throws IOException
{
final FileUploadException ex = new SizeLimitExceededException("the request was rejected because its size (" + pCount + ") exceeds the configured maximum" + " (" + pSizeMax + ")", pCount, pSizeMax);
throw new FileUploadIOException(ex);
}
};
}
else if (FileUploadBase.this.sizeMax >= 0L && requestSize > FileUploadBase.this.sizeMax)
{
throw new SizeLimitExceededException("the request was rejected because its size (" + requestSize + ") exceeds the configured maximum (" + FileUploadBase.this.sizeMax + ")", requestSize, FileUploadBase.this.sizeMax);
}
}
String charEncoding = FileUploadBase.this.headerEncoding;
if (charEncoding == null)
{
charEncoding = ctx.getCharacterEncoding();
}
this.boundary = FileUploadBase.this.getBoundary(contentType);
if (this.boundary == null)
{
throw new FileUploadException("the request was rejected because no multipart boundary was found");
}
this.notifier = new MultipartStream.ProgressNotifier(FileUploadBase.this.listener, ctx.getContentLength());
(this.multi = new MultipartStream(input, this.boundary, this.notifier)).setHeaderEncoding(charEncoding);
this.skipPreamble = true;
this.findNextItem();
}
//......
}
// commons - fileupload - 1.2.1.jar org.apache.commons.fileupload FileUploadException.java
public FileUploadException(final String msg, final Throwable cause)
{
super(msg);
this.cause = cause;
}
我的博客即将同步至腾讯云+社区,邀请大家一同入驻。