Springboot文件上传的一些源码理解

//请求进来
DispatcherServlet.doDispatch()1032行
    processedRequest = checkMultipart(request);1044return this.multipartResolver.resolveMultipart(request);1210StandardMultipartHttpServletRequest.parseRequest()93Collection<Part> parts = request.getParts();95Request.parseParts()2750List<FileItem> items = upload.parseRequest(new ServletRequestContext(this));2834FileUploadBase.parseRequest()296Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer);317Streams.copy()89public static long copy(final InputStream inputStream,
            final OutputStream outputStream, final boolean closeOutputStream,
            final byte[] buffer)
    throws IOException {
        try (OutputStream out = outputStream;
              InputStream in = inputStream) {
            long total = 0;
            for (;;) {
                final int res = in.read(buffer);
                if (res == -1) {
                    break;
                }
                if (res > 0) {
                    total += res;
                    if (out != null) {
                        out.write(buffer, 0, res);
                    }
                }
            }
            if (out != null) {
                if (closeOutputStream) {
                    out.close();
                } else {
                    out.flush();
                }
            }
            in.close();
            return total;
        }
    }

结论:
1.在进入controller时,上传文件已通过Stream.copy存在服务器磁盘上或者内存里了,存储位置由fileSizeThreshold决定,文件大小达到该阈值,将写入临时目录,默认为0,即所有文件都会直接写入磁盘临时文件中;小于该阈值则放在内存里。判断逻辑就在Stream.copy中的out.write里。

2.看了下Stream.copy的代码,是用一个InputStream和一个OutputStream进行上传文件落盘的,通过文件流的方式,就不用等待文件全部上传完了以后再处理,也就是上传一点,然后就落盘一点,尽量减少内存空间的消耗。
这里有个疑问,麻烦大佬们帮忙看看理解的对不对:
文件是否在前端发起请求时就已经在上传了,因为看到FileUploadBase.parseRequest中有一步操作final FileItemIterator iter = getItemIterator(ctx)此调用的函数里生成了MultipartStream,这个类负责是从文件流中读取文件数据的,看起来上传文件的InputStream从请求到达服务器时就一直开着的,在代码各种操作的同时,文件其实也一直在上传。如果文件较小且上传较快,当开始Stream.copy时,文件其实已经全部到达内存里了。

3.在进入controller,业务逻辑中打开了MultipartFile的InputStream后,一定要记得关闭,不然框架自动清除已落盘文件会失败,因为流处于打开状态,删除程序无法操作该文件。
删除代码见DispatcherServlet.doDispatch中1107行。

参数配置:spring.servlet.multipart开头
(1)enabled:是否开启文件上传自动配置,默认开启。
(2)location:上传文件的临时目录。
(3)maxFileSize:最大文件大小,以字节为单位,默认为1M。
(4)maxRequestSize:整个请求的最大容量,默认为10M。
(5)fileSizeThreshold:文件大小达到该阈值,将写入临时目录,默认为0,即所有文件都会直接写入磁盘临时文件中。
(6)resolveLazily:是否惰性处理请求,默认为false。

public StandardMultipartHttpServletRequest(HttpServletRequest request, boolean lazyParsing)
			throws MultipartException {

		super(request);
		if (!lazyParsing) {
			parseRequest(request);//处理上传文件
		}
	}

1)当resolveLazily为true时,我们在业务处理逻辑中,直接调用StandardMultipartHttpServletRequest接口的getXxx()方法就可以获取表单参数或表单文件信息,因为此时已经解析完成。
2)当resolveLazily为false时,在MultipartResolver.resolveMultipart()阶段并不会进行文件请求解析。也就是说,此时StandardMultipartHttpServletRequest对象的成员变量都是空值。在即将进入到Controller时,初始化方法参数,发现有MultipartFile参数,此时才会去解析上传请求,即调用parseRequest方法。

InvocableHandlerMethod
/**
	 * Invoke the method after resolving its argument values in the context of the given request.
	 * <p>Argument values are commonly resolved through
	 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}.
	 * The {@code providedArgs} parameter however may supply argument values to be used directly,
	 * i.e. without argument resolution. Examples of provided argument values include a
	 * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
	 * Provided argument values are checked before argument resolvers.
	 * <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
	 * resolved arguments.
	 * @param request the current request
	 * @param mavContainer the ModelAndViewContainer for this request
	 * @param providedArgs "given" arguments matched by type, not resolved
	 * @return the raw value returned by the invoked method
	 * @throws Exception raised if no suitable argument resolver can be found,
	 * or if the method raised an exception
	 * @see #getMethodArgumentValues
	 * @see #doInvoke
	 */
	@Nullable
	public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
		if (logger.isTraceEnabled()) {
			logger.trace("Arguments: " + Arrays.toString(args));
		}
		return doInvoke(args);
	}
AbstractMultipartHttpServletRequest
/**
	 * Obtain the MultipartFile Map for retrieval,
	 * lazily initializing it if necessary.
	 * @see #initializeMultipart()
	 */
	protected MultiValueMap<String, MultipartFile> getMultipartFiles() {
		if (this.multipartFiles == null) {
			initializeMultipart();
		}
		return this.multipartFiles;
	}
StandardMultipartHttpServletRequest
@Override
	protected void initializeMultipart() {
		parseRequest(getRequest());
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值