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

Spring二进制文件下载:控制器实现代码编写咨询

优化后的PDF下载控制器实现

看起来你正在开发一个Spring控制器来提供PDF文件的下载功能,我把你的代码补全并做了优化,同时拆解关键要点帮你理解:

import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class PdfDownloadController {

    private static final String SRC_FILE = "static/opened-events.pdf"; // 替换为你实际的文件路径

    @GetMapping("/download/opened-events")
    public @ResponseBody byte[] getOpenedEventsInPdf(HttpServletResponse response) throws IOException, URISyntaxException {
        // 加载ClassPath下的PDF文件
        ClassPathResource pdfFile = new ClassPathResource(SRC_FILE);
        
        // 校验文件是否存在
        if (!pdfFile.exists()) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "PDF文件不存在");
        }

        Path pdfPath = Paths.get(pdfFile.getURI());
        byte[] pdfBytes = Files.readAllBytes(pdfPath);

        // 设置响应头,引导浏览器正确处理文件
        response.addHeader("Content-Type", "application/pdf");
        // 强制浏览器触发下载动作,并指定文件名
        response.addHeader("Content-Disposition", "attachment; filename=\"opened-events.pdf\"");
        // 跨域配置,生产环境建议替换为具体域名
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT");

        return pdfBytes;
    }
}

关键要点解析

  • 文件读取与校验:用ClassPathResource加载项目类路径下的文件,同时增加了文件存在性校验,避免返回无效内容给前端。注意文件路径要匹配src/main/resources下的实际位置(比如放在static目录下,路径就是static/xxx.pdf)。
  • 响应头核心配置:
    • Content-Type:明确告诉浏览器返回内容是PDF类型,防止浏览器错误解析。
    • Content-Disposition:设置为attachment强制触发下载行为,同时指定下载后的文件名,提升用户体验。
    • 跨域头:如果前后端部署在不同域名下,这些配置是跨域请求的必要条件,生产环境建议不要用*,替换为具体的前端域名更安全。
  • @ResponseBody的作用:Spring会自动把返回的byte[]写入HTTP响应体,配合我们设置的响应头,就能完成完整的文件下载流程。

额外优化建议

  1. 大文件流式输出:如果PDF文件体积较大,直接读取整个文件到内存容易引发OOM,建议改用流式输出:
@GetMapping("/download/opened-events")
public void getOpenedEventsInPdf(HttpServletResponse response) throws IOException {
    ClassPathResource pdfFile = new ClassPathResource(SRC_FILE);
    if (!pdfFile.exists()) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "PDF文件不存在");
    }

    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=\"opened-events.pdf\"");
    
    // 用try-with-resources自动关闭流,避免资源泄漏
    try (var in = pdfFile.getInputStream(); var out = response.getOutputStream()) {
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
        out.flush();
    }
}
  1. 用ResponseEntity简化代码:Spring推荐用ResponseEntity封装响应内容和头,代码结构更清晰:
@GetMapping("/download/opened-events")
public ResponseEntity<byte[]> getOpenedEventsInPdf() throws IOException, URISyntaxException {
    ClassPathResource pdfFile = new ClassPathResource(SRC_FILE);
    if (!pdfFile.exists()) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND, "PDF文件不存在");
    }

    byte[] pdfBytes = Files.readAllBytes(Paths.get(pdfFile.getURI()));
    
    var headers = org.springframework.http.HttpHeaders.writableHttpHeaders();
    headers.setContentType(MediaType.APPLICATION_PDF);
    headers.setContentDisposition(org.springframework.http.ContentDisposition.attachment()
            .filename("opened-events.pdf")
            .build());
    headers.add("Access-Control-Allow-Origin", "*");
    headers.add("Access-Control-Allow-Methods", "GET, POST, PUT");
    
    return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:56:36