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

Spring下载多类型文件(PDF/图片/文档)出现损坏问题求助

解决Spring控制器下载二进制文件乱码/损坏的问题

你遇到的核心问题是**@ResponseBody注解干扰了二进制流的输出逻辑**,导致Spring的消息转换器错误地将二进制数据当成字符编码处理,最终破坏了文件结构,出现乱码和损坏。下面给你两种实用的解决方案:

方案一:移除@ResponseBody并优化响应处理

你的方法是直接操作ServletOutputStream写入二进制数据,这种场景下完全不需要@ResponseBody——这个注解会触发Spring的HttpMessageConverter对响应体进行字符编码转换,而二进制流应该直接输出,不能经过这类转换。修改后的代码如下:

public class DownloadFileController { 
    @GetMapping(value = "v0/file") 
    // 移除@ResponseBody注解,避免Spring干扰流输出
    public void downloadFile(HttpServletResponse response) { 
        try (ServletOutputStream outputStream = response.getOutputStream(); 
             InputStream inputStream = getFile()) { 
            
            // 明确标记响应为二进制流类型
            response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE); 
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"example.pdf\"");
            // 可选:设置文件大小,提升浏览器下载体验
            response.setContentLengthLong(inputStream.available());
            
            // 执行流拷贝
            IOUtils.copy(inputStream, outputStream);
            // 强制刷新输出流,确保所有二进制数据都写入响应
            outputStream.flush();
        } catch (IOException ex) { 
            throw new RuntimeException("文件下载失败", ex);
        } 
    } 
    private static InputStream getFile() { 
        return DownloadFileController.class.getResourceAsStream("/example.pdf"); 
    } 
}

方案二:使用Spring推荐的ResponseEntity<Resource>方式

这种方式更贴合Spring MVC的设计规范,不需要手动操作HttpServletResponse,能自动适配各种二进制文件类型,也能避免流处理的潜在问题:

public class DownloadFileController { 
    @GetMapping(value = "v0/file") 
    public ResponseEntity<Resource> downloadFile() { 
        try (InputStream inputStream = getFile()) {
            // 将输入流包装为Spring标准Resource对象
            Resource resource = new InputStreamResource(inputStream);
            
            // 构建响应头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setContentDispositionFormData("attachment", "example.pdf");
            headers.setContentLengthLong(inputStream.available());
            
            return ResponseEntity.ok()
                    .headers(headers)
                    .body(resource);
        } catch (IOException ex) { 
            throw new RuntimeException("文件下载失败", ex);
        } 
    } 
    private static InputStream getFile() { 
        return DownloadFileController.class.getResourceAsStream("/example.pdf"); 
    } 
}

关键注意事项

  • 所有二进制文件(PDF、图片、Word等)都不能让Spring的字符消息转换器介入处理,否则会直接破坏文件的二进制结构。
  • 确保getResourceAsStream能正确获取文件:如果是打包后的Jar包,这种路径写法(/example.pdf对应resources根目录)是可行的,注意检查文件是否存在于正确位置。
  • 优先选择ResponseEntity<Resource>的实现方式,它能更好地和Spring的生命周期整合,减少手动处理流的出错概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:08:16