You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

程序偶发失效,上传MultipartFile时触发NoSuchFileException

偶发NoSuchFileException问题排查与解决

问题代码

public HttpEntity<?> card(MultipartFile multipartFile, String name) {
    try {
        String jsonData = String.format("{\"active\": true, \"name\": \"%s\", \"watch_lists\": [\"%s\"]}",
                name, workerWatchList);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("Authorization", token);
        HttpEntity<String> addCardEntity = new HttpEntity<>(jsonData, headers);
        ResponseEntity<String> addCardResponse = new RestTemplate().exchange(
                baseUrl + "/cardurl",
                HttpMethod.POST,
                addCardEntity,
                String.class
        );
        if (addCardResponse.getStatusCode().is2xxSuccessful()) {
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode rootNode = objectMapper.readTree(addCardResponse.getBody());
            // Get the value associated with the "id" key
            String id = String.valueOf(rootNode.get("id"));
            System.out.println(id);
            HttpHeaders faceHeaders = new HttpHeaders();
            faceHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
            faceHeaders.set("Authorization", token);

            MultiValueMap<String, Object> faceBody = new LinkedMultiValueMap<>();
            faceBody.add("source_photo", multipartFile.getResource());
            faceBody.add("card", id);
            faceBody.add("mf_selector", "biggest");

            HttpEntity<MultiValueMap<String, Object>> addFaceEntity = new HttpEntity<>(faceBody, faceHeaders);
            ResponseEntity<MultiValueMap> addFaceResponse = new RestTemplate().exchange(
                    baseUrl + "/faceurl",
                    HttpMethod.POST,
                    addFaceEntity,
                    MultiValueMap.class);
            if (addFaceResponse.getStatusCode().is2xxSuccessful()) {
                return ResponseEntity.ok(id);
            } else {
                return new ResponseEntity<>(addFaceResponse.getBody(), HttpStatus.INTERNAL_SERVER_ERROR);
            }
        } else {
            return new ResponseEntity<>(addCardResponse.getBody(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    } catch (Exception e) {
        // Handle any other exceptions
        return ResponseEntity.ok("Error: " + e.getMessage());
    }
}

问题描述

上述Java程序偶发失效,添加MultipartFile文件时报错:
java.nio.file.NoSuchFileException: C:\Users\Acer\AppData\Local\Temp\tomcat.8080.12753633009760934525\work\Tomcat\localhost\ROOT\upload_7761731e_ac13_4cc4_8cda_e9c930bf8a50_00000000.tmp

原因分析

核心原因

问题出在multipartFile.getResource()的使用逻辑上:

  • Spring处理上传文件时,默认会将文件暂存到Tomcat的临时目录,multipartFile.getResource()返回的是指向该临时文件的FileSystemResource。
  • Tomcat会定期清理临时目录文件,或在当前请求处理完成后自动删除临时文件。如果第一个RestTemplate.exchange()请求耗时较长,刚好赶上Tomcat的清理时机,就会导致后续访问临时文件时出现文件不存在的异常。

次要风险点

代码中还有两个可能加重问题的细节:

  1. 重复创建RestTemplate实例:new RestTemplate()会重复初始化HTTP客户端资源,延长请求处理时间,增加临时文件被清理的概率。
  2. ID解析错误:String.valueOf(rootNode.get("id"))会将JSON节点转为带引号的字符串(比如"123"),而非纯ID值,可能导致后续接口请求失败。

修复方案

1. 替换临时文件依赖,使用内存存储文件内容

将multipartFile.getResource()替换为ByteArrayResource,直接将文件内容加载到内存,彻底摆脱对Tomcat临时文件的依赖:

// 替换原faceBody.add("source_photo", multipartFile.getResource()); 这一行
ByteArrayResource photoResource = new ByteArrayResource(multipartFile.getBytes()) {
    @Override
    public String getFilename() {
        return multipartFile.getOriginalFilename(); // 必须重写,确保Multipart请求能识别文件名
    }
};
faceBody.add("source_photo", photoResource);

2. 优化RestTemplate使用

不要每次创建新的RestTemplate实例,改为通过Spring注入复用:

// 在类中注入RestTemplate
@Autowired
private RestTemplate restTemplate;

// 替换代码中所有new RestTemplate()为restTemplate
ResponseEntity<String> addCardResponse = restTemplate.exchange(...);

3. 修正ID解析逻辑

使用asText()获取纯ID字符串,避免带引号的问题:

String id = rootNode.get("id").asText();

备选方案:自定义上传临时目录

如果文件过大不适合加载到内存,可以配置Spring的上传临时目录,避免Tomcat自动清理:
在application.properties中添加:

# 自定义上传临时目录
spring.servlet.multipart.location=/your/custom/temp/path
# 可根据需求调整文件大小限制
spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=50MB

内容的提问来源于stack exchange,提问作者Ma'murjon To'laganov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:39:49