先来讲述一下commons-fileupload实现上传的流程。
1.添加依赖。
<!-- commons 文件操作 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<!-- FILE UPLOAD begin -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
1.创建一个文件上传解析器
我们知道上传有一些限制条件,比如上传文件大小,显示的上传进度等,如果是多文件上传,还有总文件大小的限制条件。
由于是个组件,这些参数都需要开发者自己定义,因此,在创建文件上传解析器时,我才用了builder模式。
public class UploadBuilder {
public ServletFileUpload upload = new ServletFileUpload();
public UploadBuilder(Builder builder){
upload.setSizeMax(builder.sizeMax);
upload.setFileSizeMax(builder.fileSizeMax);
upload.setHeaderEncoding(builder.headerEncoding);
upload.setFileItemFactory(builder.factory);
upload.setProgressListener(builder.listener);
}
public static final class Builder {
//总文件大小上限,默认无限大
private long sizeMax = -1;
//单个文件的大小上限,默认无限大
private long fileSizeMax = -1;
//编码方式
private String headerEncoding;
private DiskFileItemFactory factory;
//进度条Listener
private UploadProgressListener listener;
public Builder(DiskFileItemFactory factory) {
this.factory = factory;
}
public Builder sizeMax(long value){
this.sizeMax = value;
return this;
}
public Builder fileSizeMax(long value){
this.fileSizeMax = value;
return this;
}
public Builder headerEncoding(String value){
this.headerEncoding = value;
return this;
}
public Builder listener(UploadProgressListener listener){
this.listener = listener;
return this;
}
public ServletFileUpload build() {
return new UploadBuilder(this).upload;
}
}
}
由于需求要展示上传文件的进度条,因此在这里设置了进度条Listener
public class UploadProgressListener implements ProgressListener {
private HttpSession session;
public UploadProgressListener(HttpServletRequest request) {
this.session = request.getSession();
}
@Override
public void update(long pBytesRead, long pContentLength, int pItems) {
/**
* 进度条信息,自行定义
*/
//进度条回写到session中
session.setAttribute("processInfo", info);
}
}
2.使用ServletFileUpload解析器解析上传数据
public default ParseUploadInfoModel parseFileItems(ServletFileUpload upload,HttpServletRequest request){
List<FileItem> items = null;
if (!ServletFileUpload.isMultipartContent(request)) {
return new ParseUploadInfoModel(UploadFileCodeEnmu.NOT_MULTIPART_CONTENT,null);
}
try {
items = upload.parseRequest(request);
} catch (FileUploadBase.SizeLimitExceededException e){
return new ParseUploadInfoModel(UploadFileCodeEnmu.Q_EXCEED_SIZE_LIMIT,null);
} catch (FileUploadBase.FileSizeLimitExceededException e) {
return new ParseUploadInfoModel(UploadFileCodeEnmu.F_EXCEED_SIZE,null);
}catch (FileUploadException e) {
}
return new ParseUploadInfoModel(UploadFileCodeEnmu.SUCCESS,items);
}
这里获取到了解析数据,但是由于框架使用的是SpringBoot,其中spring mvc在处理请求的时候,有一个判断是否是上传请求,如果是上传请求,则进行解析转为MultipartHttpServletRequest请求。这样一来,我通过以上方式去获取请求参数时,就没法获取到了。源码分析如下:
springmvc判断是否是上传请求:
protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml");
}
else if (hasMultipartException(request) ) {
logger.debug("Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering");
}
else {
try {
return this.multipartResolver.resolveMultipart(request);
}
catch (MultipartException ex) {
if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
logger.debug("Multipart resolution failed for error dispatch", ex);
// Keep processing error dispatch with regular request handle below
}
else {
throw ex;
}
}
}
}
// If not returned before: return original request.
return request;
}
主要通过this.multipartResolver.isMultipart(request)来判断的,这里MultipartResolver.class接口有两个实现类:CommonsMultipartResolver.class和StandardServletMultipartResolver.class。我们看看CommonsMultipartResolver.class的实现。
@Override
public boolean isMultipart(HttpServletRequest request) {
return ServletFileUpload.isMultipartContent(request);
}
ServletFileUpload.isMultipartContent(request);
public static final boolean isMultipartContent(
HttpServletRequest request) {
if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
return false;
}
return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}
public static final boolean isMultipartContent(RequestContext ctx) {
String contentType = ctx.getContentType();
if (contentType == null) {
return false;
}
if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
return true;
}
return false;
}
其实就是根据两个条件:1.一个是请求方式是POST。2.请求的contentType是multipart/,这个是前端页面定义上传的时候就必须定义好的。
那我们这里通过这种方式判断是否是上传请求,如果是上传请求,就进行解析并封装成MultipartHttpServletRequest.class。
@Override
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
Assert.notNull(request, "Request must not be null");
if (this.resolveLazily) {
return new DefaultMultipartHttpServletRequest(request) {
@Override
protected void initializeMultipart(