Spring之文件上传与MultipartFile

转载自:https://hewei0928.github.io/2018/06/20/Spring%E4%B9%8B%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E4%B8%8EMultipartFile/

叙述

Spring 文件上传使用的总结及MultipartFile的解析

解决方案

MultipartFile

单文件上传

@RequestMapping(value = "/UploadOneServlet.do", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestParam("multipartFile") MultipartFile multipartFile) {
    //...文件处理代码
}

后端中的对应方法中使用MultipartFile类型参数进行接收。必须添加@RequestParam("multipartFile")参数,否则报错。@RequestParamvalue与前端imput标签中的name对应。

多个文件上传

@RequestMapping(value = "/UploadOneServlet.do", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestParam("multipartFile") List<MultipartFile> multipartFile) {
    //...文件处理代码
}

后台使用List,即可前台上传的多个文件。如果使用单个MultipartFile类型进行接收则只能接收一个文件。

混合使用

@RequestMapping(value = "/UploadOneServlet.do", method = RequestMethod.POST)
@ResponseBody
public String upload(@RequestParam("multipartFile") List<MultipartFile> multipartFiles, @RequestParam("multipartFiles") MultipartFile multipartFile) {
}

后台使用一个List<MultiPartFile>类型和MultiPartFile类型进行接收。@RequestParamvalue与前端imput标签中的name一一对应。

解析多个文件

private void upload(List<MultipartFile> multipartFiles) throws Exception{
	for (MultipartFile multipartFile : multipartFiles) {
		String fileName = multipartFile.getOriginalFilename();
		String filePath = ContextLoader.getCurrentWebApplicationContext().getServletContext().getRealPath("/") + "fileUpload";
		String fileTotalName = filePath + File.separator + fileName;
		File f = new File(fileTotalName);
		multipartFile.transferTo(f);
	}
}

HttpServletRequest

使用HttpServletRequest接收

@RequestMapping(value = "/UploadOneServlet.do", method = RequestMethod.POST)
@ResponseBody
public String upload(HttpServletRequest request){
	List<MultipartFile> files = new ArrayList<>();
	MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
	Iterator<String> a = multipartHttpServletRequest.getFileNames();//返回的数量与前端input数量相同, 返回的字符串即为前端input标签的name
	while (a.hasNext()) {
		String name = a.next();
		List<MultipartFile> multipartFiles = multipartHttpServletRequest.getFiles(name);//获取单个input标签上传的文件,可能为多个
		files.addAll(multipartFiles);
	}
	upload(files);
	return "success";
}

MultiPartFile类解析

Spring MVC中要使用MultipartFile进行文件上传,需要先注入MultipartResolver类型的Bean

之前解析DispatcherServlet类时得知所有最终会调用这个类中的doDispatch方法对request进行处理

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
				//...剩余代码
        }
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

可以看到processedRequest = checkMultipart(request)对reuqest进行判断

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);
				}
				else {
					throw ex;
				}
			}
		}
	}
	return request;
}

通过以上方法可以看到以下逻辑:

  1. 判断用户是否注入了MultipartResolver类型的Bean,如果注入了则使用它去检测当前请求是否为文件上传请求
  2. 如果为文件上传请求,就通过MultipartResolver去处理当前请求。
  3. 返回处理后的请求或为处理的请求。

接下来看看Spring是如何判断当前请求是文件上传请求的, 这里主要看CommonsMultipartResolver

public boolean isMultipart(HttpServletRequest request) {
	return (request != null && ServletFileUpload.isMultipartContent(request));
}

这里直接使用了Apache中的ServletFileUpload

public static final boolean isMultipartContent(
        HttpServletRequest request) {
    if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
        return false;
    }
    return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
}

这里可以看到request请求必须是POST类型,如果是,则用FileUploadBase进行进一步判断

public static final boolean isMultipartContent(RequestContext ctx) {
    String contentType = ctx.getContentType();
    if (contentType == null) {
        return false;
    }
    if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {// MULTIPART = "multipart/"
        return true;
    }
    return false;
}

以上方法说明:
只有当当前请求的contentType是以”multipart/“开头的时候,才会将此请求当做文件上传的请求。

综上,Spring判断当前请求是否是文件上传请求主要有两个条件:

  1. 当前请求为POST请求
  2. 当前请求的contextType必须以”multipart/“开头
    这正符合了前端<form>标签中的设置。

如果是文件上传请求,则会调用multipartResolver.resolveMultipart(request)对请求进行解析

public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
    MultipartParsingResult parsingResult = parseRequest(request);
	return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
    					parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}

可以看到任然是调用parseRequest方法对request进行解析,并将结果存入MultipartParsingResult

protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
	String encoding = determineEncoding(request);
	FileUpload fileUpload = prepareFileUpload(encoding);
	try {
	    // 获取到上传文件的信息 一个FileItem对应一个文件,FileItem的fieldName与input框的name对应
		List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
		//将其存入MultipartParsingResult类中
		return parseFileItems(fileItems, encoding);
	}
	catch (FileUploadBase.SizeLimitExceededException ex) {
		throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
	}
	catch (FileUploadBase.FileSizeLimitExceededException ex) {
		throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);
	}
	catch (FileUploadException ex) {
		throw new MultipartException("Failed to parse multipart servlet request", ex);
	}
}
protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
		MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
		Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
		Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();

		// Extract multipart files and multipart parameters.
		for (FileItem fileItem : fileItems) {
			if (fileItem.isFormField()) {
				//...省略代码
			}
			else {
				// multipart file field
				CommonsMultipartFile file = createMultipartFile(fileItem);
				// file.getName()得到fileItem.getfieldName  map键位input框的name, 值为具体文件类
				multipartFiles.add(file.getName(), file);
				if (logger.isDebugEnabled()) {
					logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() +
							" bytes with original filename [" + file.getOriginalFilename() + "], stored " +
							file.getStorageDescription());
				}
			}
		}
		return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
}

初始化了MultipartParsingResult对象之后就能根据该对象去初始化MultipartHttpServletRequest对象,供接口中调用

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值