程序偶发失效,上传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的清理时机,就会导致后续访问临时文件时出现文件不存在的异常。
次要风险点
代码中还有两个可能加重问题的细节:
- 重复创建RestTemplate实例:
new RestTemplate()会重复初始化HTTP客户端资源,延长请求处理时间,增加临时文件被清理的概率。 - 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
相关产品推荐
相关产品推荐

