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

Spring控制器通过PDFBox导出PDF至用户时返回空文件的问题

解决Spring控制器导出PDFBox生成PDF为空的问题

嘿,我来帮你搞定这个导出PDF返回空文件的问题!先看看你代码里几个关键的问题,这些就是导致空文件的元凶:

1. 请求方法与响应类型完全不匹配

你用了PUT请求方法,而且produces设置成了APPLICATION_JSON_VALUE——但我们要导出的是PDF啊!PUT一般用来更新资源,下载用GET或者POST更合适;同时produces得改成PDF的媒体类型MediaType.APPLICATION_PDF_VALUE,不然服务器会告诉浏览器返回的是JSON,浏览器自然解析不了PDF。

2. 没给HttpServletResponse设置正确的响应头

浏览器需要知道返回的内容是什么类型、要不要触发下载,你得设置Content-Type和Content-Disposition这些头,不然浏览器可能把PDF当成空白内容处理。

3. 核心问题:没把PDDocument写入响应输出流

你创建了PDDocument,但看起来没把它写到response的输出流里,这肯定会返回空文件啊!另外还要记得关闭资源,别搞内存泄漏。

修正后的完整代码示例

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@RequestMapping("/reports")
public class PDFReportController {

    @PostMapping(value = "/pdfReport", produces = MediaType.APPLICATION_PDF_VALUE)
    public void downloadPDFReport(HttpServletResponse response, @RequestBody PDFReport pdfReport) throws IOException {
        // 1. 这里替换成你从storageManager获取内容并构建PDF的逻辑
        PDDocument document = new PDDocument();
        PDPage page = new PDPage();
        document.addPage(page);

        // 示例:把storageManager返回的StringBuilder内容写入PDF
        String content = storageManager.getPDF(pdfReport).toString();
        try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
            contentStream.beginText();
            contentStream.setFont(PDType1Font.HELVETICA, 12);
            contentStream.newLineAtOffset(50, 750); // 设置起始位置
            // 换行处理内容,避免超出页面
            String[] lines = content.split("\n");
            for (String line : lines) {
                contentStream.showText(line);
                contentStream.newLineAtOffset(0, -15); // 每行下移15个单位
            }
            contentStream.endText();
        } catch (IOException e) {
            e.printStackTrace();
            // 这里可以加异常处理,比如返回错误给前端
        }

        // 2. 设置响应头,告诉浏览器这是可下载的PDF
        response.setContentType(MediaType.APPLICATION_PDF_VALUE);
        response.setHeader("Content-Disposition", "attachment; filename=\"pdf_report.pdf\"");
        // 可选:如果能获取PDF的大小,可以设置Content-Length,让浏览器显示下载进度
        // response.setContentLength((int) document.getDocument().getLength());

        // 3. 把PDF写入响应输出流,这是核心!
        document.save(response.getOutputStream());
        
        // 4. 关闭资源,避免内存泄漏
        document.close();
        response.getOutputStream().flush();
        response.getOutputStream().close();
    }
}

几个必须注意的点

  • 响应头一定要对:Content-Disposition里的attachment会让浏览器弹出下载窗口,filename是用户下载后看到的文件名;Content-Type必须是application/pdf,不然浏览器可能识别错误。
  • 资源要管好:用try-with-resources处理PDPageContentStream,它会自动关闭;写完PDF后一定要关闭PDDocument,不然内存会爆。
  • 请求方法选对:如果你的PDF不需要传复杂的请求体,用GET更直观;如果必须传JSON参数,就用POST,别用PUT了,不符合REST规范。
  • 输出流要刷干净:写完后调用flush()确保所有内容都发送到客户端,再关闭流。

要是你storageManager.getPDF返回的内容有特殊排版需求,比如加粗、换行,就调整PDPageContentStream的逻辑就行,核心就是把内容正确写入PDF,再输出到响应流里。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:13:43