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响应体,配合我们设置的响应头,就能完成完整的文件下载流程。
额外优化建议
- 大文件流式输出:如果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(); } }
- 用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
相关产品推荐
相关产品推荐

