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

Spring Boot返回文件时FileNotFoundException未捕获致500错误的解决问询

问题原因分析

你遇到的核心问题是:控制器的try-catch根本没机会捕获这个FileNotFoundException。从堆栈追踪能看出,异常是在控制器方法返回ResponseEntity之后,Spring的ResourceHttpMessageConverter尝试计算响应的Content-Length时抛出的——这时候控制器代码已经执行完毕,try-catch的作用范围早就过去了。

解决方案

有两种可靠处理方式,优先推荐第一种:

方式1:控制器内提前检查文件状态

在构建Resource并返回之前,主动检查文件是否存在且可读,把异常提前到控制器的try块内抛出。

修改后的控制器代码:

@GetMapping("analysis/daily/{year}/{month}/{day}/{mode}")
public ResponseEntity<Resource> getDailyAnalysis(
        @PathVariable("year") int year, 
        @PathVariable("month") int month,
        @PathVariable("day") int day, 
        @PathVariable("mode") AnalysisSelection type) {
    try {
        Path fileBasePath = Path.of("Analysis", "ZIP", "Daily",
                String.valueOf(year),
                String.valueOf(month),
                String.valueOf(day));
        switch (type) {
            case ByItem -> fileBasePath = fileBasePath.resolve("SingleItemAnalysis.zip");
            case ByAuthor -> fileBasePath = fileBasePath.resolve("Author.zip");
            case ByType -> fileBasePath = fileBasePath.resolve("Type.zip");
            case ByIndustry -> fileBasePath = fileBasePath.resolve("Industry.zip");
            default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
        }
        
        // 提前检查文件是否存在且可读
        if (!Files.exists(fileBasePath) || !Files.isReadable(fileBasePath)) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND);
        }
        
        Resource resource = new UrlResource(fileBasePath.toUri());
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType("application/zip"))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(resource);
    } catch (Exception e) {
        if (e instanceof ResponseStatusException) {
            throw e;
        }
        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
}

这样做的好处是:在返回响应前主动校验文件状态,把异常控制在控制器的try块范围内,确保能被捕获并返回正确的404。

方式2:全局异常捕获

如果需要统一处理整个应用中的FileNotFoundException,可添加全局异常处理器:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(FileNotFoundException.class)
    public ResponseEntity<String> handleFileNotFound(FileNotFoundException ex) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("请求的文件不存在");
    }
}

这种方式适合统一处理所有文件找不到异常的场景,但不如第一种精准,可能会捕获到其他业务场景的FileNotFoundException。

关于JWT过滤器的误解

堆栈里指向JWT过滤器的filterChain.doFilter(request, response)只是因为过滤器是请求链的一部分,异常是在后续请求处理流程中抛出的,和JWT过滤器本身没有关系,无需修改JWT相关代码。

内容的提问来源于stack exchange,提问作者CodingPandaBear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 04:45:03