行业资讯 2025年08月6日
0 收藏 0 点赞 682 浏览 3350 个字
摘要 :

文章目录 一、MultipartFile解析流程 二、上传配置详解 三、Feign上传文件的问题及解决方案 (一)调用方实现 (二)服务器方实现 四、Spring对application/octet-str……




  • 一、MultipartFile解析流程
  • 二、上传配置详解
  • 三、Feign上传文件的问题及解决方案
    • (一)调用方实现
    • (二)服务器方实现
  • 四、Spring对application/octet-stream的处理

Spring Boot文件上传会使用到MultipartFile,下面深入了解一下它的运行机制、配置要点以及实际应用中的问题与解决方案。

一、MultipartFile解析流程

当客户端发起文件上传请求,也就是发送multipart/form-data请求时,整个处理流程如下:

  1. Servlet容器处理:Servlet容器接收到请求后,会启动HTTP协议解析工作。它根据请求头中的Content-Type来识别这是一个multipart类型的请求,然后创建临时存储结构。这个临时存储结构有可能在内存中,也可能在磁盘上,具体取决于后续配置。处理完这些后,Servlet容器会将解析后的数据封装成Request对象。
  2. Spring MVC解析:封装好的Request对象进入Spring MVC框架后,会触发MultipartResolver进行解析。这里有个关键的点要注意,只有在调用MultipartFile.getBytes()方法时,文件数据才会真正被加载到内存中。

用序列图表示的话,就是:Spring Boot中MultipartFile实现原理机制详解

二、上传配置详解

在Spring Boot项目中,可以通过配置文件对文件上传进行设置。以YAML配置文件为例:

spring:  
  servlet:  
    multipart:  
      enabled: true  
      file-size-threshold: 1MB # 1MB 以下用内存,以上用磁盘  
      max-file-size: 5GB # 单个文件最大  
      max-request-size: 10GB # 整个请求最大  
      location: /data/tmp # 指定专用临时目录  
  • enabled:开启文件上传功能。
  • file-size-threshold:用于设定文件存储策略的阈值。小于这个阈值(1MB)的文件,会优先存储在内存中;大于这个阈值的文件,则会存储到磁盘上。
  • max-file-size:限制单个文件的最大上传大小。
  • max-request-size:规定整个上传请求的最大大小。
  • location:指定文件上传过程中临时存储的目录。

三、Feign上传文件的问题及解决方案

在实际开发中,使用Feign上传大文件时可能会遇到问题。比如,会出现“Handler dispatch failed; nested exception is java.lang.OutOfMemoryError: Required array length 2147483639 + 9 is too large”这样的报错信息。这是因为Feign在处理大文件上传时存在一些局限性,此时可以考虑使用RestTemplate来实现文件上传。

(一)调用方实现

/**  
 * 使用@LoadBalanced注解不能进行流式上传  
 * @return  
 */  
public RestTemplate getUploadRestTemplate() {  
    // 使用 Apache HttpClient 作为底层实现,使用 流式上传  
    CloseableHttpClient httpClient = HttpClients.custom()  
      .setMaxConnTotal(100) // 最大连接数  
      .setMaxConnPerRoute(20) // 每个路由的最大连接数  
      .build();  

    HttpComponentsClientHttpRequestFactory factory =  
        new HttpComponentsClientHttpRequestFactory(httpClient);  
    factory.setBufferRequestBody(false);  
    factory.setConnectTimeout(30_000); // 连接超时 30s  
    factory.setReadTimeout(300_000); // 读取超时 5min(大文件需延长)  
    return new RestTemplate(factory);  
}  
  
@PostMapping(value = \"/file/upload\")  
public String uploadStream(@RequestParam(\"file\") MultipartFile file) throws IOException {  
    // 包装文件流为Resource(关键:实现contentLength()返回 -1)  
    int bufSize = 64*1024; //默认8kb  
    Resource resource = new InputStreamResource(new BufferedInputStream(file.getInputStream(),bufSize)) {  
        @Override  
        public long contentLength() {  
            return -1; // 触发分块传输  
        }  

        @Override  
        public String getFilename() {  
            return file.getName(); // 确保服务器能获取文件名  
        }  
    };  

    // 构建Multipart请求体  
    // 这里没有调用file.transferTo(),可能是原代码有误,正常上传文件需要处理文件存储逻辑

    // 设置请求头  
    HttpHeaders headers = new HttpHeaders();  
    headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);  
    ResponseEntity<String> response =  
        //手动进行负载  
        getUploadRestTemplate().exchange(\"http://localhost:8081/upload-file4?name=\"+file.getOriginalFilename(),  
        HttpMethod.POST,  
        new HttpEntity<>(resource,headers), String.class);  

    System.out.println(JSON.toJSONString(response));  

    return \"success\";  
}  

这里需要注意,添加了@LoadBalanced注解的RestTemplate会缓存请求数据,在处理大文件上传时容易导致内存溢出,所以在处理大文件上传场景时不能使用该注解。如果是在微服务系统中使用,就需要自行实现负载均衡逻辑。

(二)服务器方实现

//这样直接从request获取文件流,就不解析MultipartFile,效率也要好点  
@PostMapping(value = \"/upload-file4\", consumes = \"application/octet-stream\")  
public String uploadStream3(HttpServletRequest request,@RequestParam(\"name\") String name) throws Exception {  
    try (InputStream inputStream = request.getInputStream()) {  
        Files.copy(inputStream, Paths.get(\"d:/\" + System.currentTimeMillis() + \"_\" + name));  
        return \"Upload success!\";  
    }  
}  

这种方式直接从HttpServletRequest中获取文件流,绕过了MultipartFile的解析过程,在一定程度上提高了效率。

四、Spring对application/octet-stream的处理

Spring Boot在处理application/octet-stream请求类型时,主要使用ResourceHttpMessageConverter类。

  1. 读过程:它既支持流式读取数据,也支持将数据全部加载到字节数组中。不过,将数据全部加载到字节数组这种方式,在处理大文件时可能会导致内存溢出问题。
  2. 写过程:会先将数据写入一个字节数组中,同样存在内存溢出的风险。

在实际开发中,需要根据具体的业务场景和文件大小,合理选择处理方式,避免出现内存溢出等问题,确保系统的稳定运行。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/10510.html

管理员

相关推荐
2025-08-06

文章目录 一、Reader 接口概述 1.1 什么是 Reader 接口? 1.2 Reader 与 InputStream 的区别 1.3 …

988
2025-08-06

文章目录 一、事件溯源 (一)核心概念 (二)Kafka与Golang的优势 (三)完整代码实现 二、命令…

465
2025-08-06

文章目录 一、证明GC期间执行native函数的线程仍在运行 二、native线程操作Java对象的影响及处理方…

348
2025-08-06

文章目录 一、事务基础概念 二、MyBatis事务管理机制 (一)JDBC原生事务管理(JdbcTransaction)…

456
2025-08-06

文章目录 一、SnowFlake算法核心原理 二、SnowFlake算法工作流程详解 三、SnowFlake算法的Java代码…

517
2025-08-06

文章目录 一、本地Jar包的加载操作 二、本地Class的加载方法 三、远程Jar包的加载方式 你知道Groo…

832
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号