SpringBoot核心技术-Web开发-文件上传

一、页面表单

<form role="form" action="/upload" method="post" enctype="multipart/form-data">
        <input type="email" name="email">
        <input type="text" name="username">

        <input type="file" name="headerImg" >
        <!--多个文件上传加multiple-->
        <input type="file" name="photos" multiple>
        <button type="submit" >Submit</button>
</form>

二、文件上传代码

    /**
     * MultipartFile 自动封装上传过来的文件
     * @param email
     * @param username
     * @param headerImg
     * @param photos
     * @return
     */
    @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg") MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {

        log.info("上传的信息:email={},username={},headerImg={},photos={}",
                email,username,headerImg.getSize(),photos.length);

        if(!headerImg.isEmpty()){
            //保存到文件服务器,OSS服务器
            String originalFilename = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
        }

        if(photos.length > 0){
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()){
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("H:\\cache\\"+originalFilename));
                }
            }
        }


        return "main";
    }

 配置文件修改:

#文件上传的相关配置,第一个是单个文件最大大小,第二个是总的文件最大大小
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB

三、自动配置原理

文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties配置属性类

//MultipartAutoConfiguration类中
@ConditionalOnMissingBean(MultipartResolver.class)
	public StandardServletMultipartResolver multipartResolver() {
		StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
		multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());
		return multipartResolver;
	}

自动配置好了 StandardServletMultipartResolver 【文件上传解析器(判断是否是文件请求)】

文件上传原理:

1、请求进来 使用文件上传解析器判断(checkMultipart中的isMultipart方法判断)并封装(resolveMultipart返回MultipartHttpServletRequest)文件上传请求

//doDispatch方法中,先判断是否是文件请求
processedRequest = this.checkMultipart(request);
//通过请求是否被包装,确定是否是文件上传请求
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);

 文件上传解析器multipartResolver只有一个StandardServletMultipartResolver

 checkMultipart的具体代码:

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        //判断是否是文件请求
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                if (DispatcherType.REQUEST.equals(request.getDispatcherType())) {
                    this.logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
                }
            } else if (this.hasMultipartException(request)) {
                this.logger.debug("Multipart resolution previously failed for current request - skipping re-resolution for undisturbed error rendering");
            } else {
                try {
                    //封装文件请求,变为MultipartHttpServletRequest请求类型
                    return this.multipartResolver.resolveMultipart(request);
                } catch (MultipartException var3) {
                    if (request.getAttribute("javax.servlet.error.exception") == null) {
                        throw var3;
                    }
                }

                this.logger.debug("Multipart resolution failed for error dispatch", var3);
            }
        }

this.multipartResolver.isMultipart(request):判断是否是文件请求

return this.multipartResolver.resolveMultipart(request);封装文件请求为请求类型MultipartHttpServletRequest,具体的是StandardMultipartHttpServletRequest。

2、参数解析器来解析请求中的文件内容封装成MultipartFile

最终选择RequestPartMethodArgumentResolver参数解析器解析,

 RequestPartMethodArgumentResolver调用resolveArgument方法,如下是方法中的关键代码:

Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);

 而上面这个resolveMultipartArgument方法关键代码如下:

//如果参数是文件数组,从请求的Map中 拿取请求的文件 赋给请求参数
parts = ((MultipartHttpServletRequest)multipartRequest).getFiles(name);

 

3、StandardServletMultipartResolver 将request中的文件信息封装为一个Map。

StandardServletMultipartResolver 将普通request包装为MultipartHttpServletRequest(接口),更具体的是StandardMultipartHttpServletRequest

public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
        return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}

StandardMultipartHttpServletRequest的parseRequest方法中将request中文件信息封装为一个Map,上述的getFiles方法就是从Map中拿取对应的文件

//AbstractMultipartHttpServletRequest类
public List<MultipartFile> getFiles(String name) {
        List<MultipartFile> multipartFiles = (List)this.getMultipartFiles().get(name);
        return multipartFiles != null ? multipartFiles : Collections.emptyList();
}

getMultipartFiles()会返回一个Map,MultiValueMap<String, MultipartFile>

protected MultiValueMap<String, MultipartFile> getMultipartFiles() {
        if (this.multipartFiles == null) {
            this.initializeMultipart();
        }

        return this.multipartFiles;
}
//StandardMultipartHttpServletRequest类 上面代码的initializeMultipart具体实现
private void parseRequest(HttpServletRequest request) {
        try {
            Collection<Part> parts = request.getParts();
            this.multipartParameterNames = new LinkedHashSet(parts.size());
            MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap(parts.size());
            Iterator var4 = parts.iterator();

            while(var4.hasNext()) {
                Part part = (Part)var4.next();
                String headerValue = part.getHeader("Content-Disposition");
                ContentDisposition disposition = ContentDisposition.parse(headerValue);
                String filename = disposition.getFilename();
                if (filename != null) {
                    if (filename.startsWith("=?") && filename.endsWith("?=")) {
                        filename = StandardMultipartHttpServletRequest.MimeDelegate.decode(filename);
                    }

                    files.add(part.getName(), new StandardMultipartHttpServletRequest.StandardMultipartFile(part, filename));
                } else {
                    this.multipartParameterNames.add(part.getName());
                }
            }

            this.setMultipartFiles(files);
        } catch (Throwable var9) {
            this.handleParseFailure(var9);
        }

 

FileCopyUtils,实现文件流的拷贝

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值