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

ItextPdf PDF下载异常:大尺寸文件下载后损坏

问题分析与修复方案

你的PDF下载在大文件场景下损坏,核心问题出在响应头时机、流的重复获取与关闭顺序上,以下是具体问题点和修复方案:

关键问题点

  1. 响应头设置时机错误:在已经写入PDF内容后才设置Content-Type和Content-Disposition,此时响应头可能已发送给客户端,导致文件类型识别异常或头信息丢失。
  2. 重复获取输出流:先通过PdfWriter绑定response.getOutputStream(),后续又在try-with-resources中重新获取流实例,两个流的操作会互相干扰,导致大文件内容写入不完整。
  3. 流关闭顺序错误:document.close()会自动关闭其关联的输出流,后续手动关闭out属于重复操作,可能导致流提前截断,大文件未完全写入就终止。

修复后的代码

public void downloadPdfSample() {
    Object object = FacesContext.getCurrentInstance().getExternalContext().getRequest();
    if (!(object instanceof HttpServletRequest)) {
        return;
    }

    HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();

    try {
        int countTotalPratical = 0;
        List<SubjectResultDto> subjectResultDtos = sortSubjectOrder(marksLedgerDataModelJsf.getSubjectResultDtos());

        for (SubjectResultDto subjectResultDto : subjectResultDtos) {
            if (subjectResultDto.isHasPractical()) {
                countTotalPratical++;
            }
        }
        Collections.sort(subjectResultDtos, SubjectResultDto.comparatorBySubjectProgramSemesterMapOrder);
        marks = false;
        grade = false;
        marksAndGrade = false;
        showGraceMarks = false;
        int subjectResultSize = 0;
        float length = 0;
        if ("Marks".equals(marksLedgerDataModelJsf.getReportType())) {
            marks = true;
            subjectResultSize = 1 + subjectResultDtos.size() + countTotalPratical;
            length = 410 + (subjectResultDtos.size()) * 80 + countTotalPratical * 65;
        }
        if ("Grade".equals(marksLedgerDataModelJsf.getReportType())) {
            grade = true;
            subjectResultSize = 2 * subjectResultDtos.size() + 2 * countTotalPratical;
            length = 330 + (subjectResultDtos.size()) * 90 + countTotalPratical * 95;
        }//other codes to set length as required
        length = Math.min(length, 1550);

        int breadth = 800;
        Rectangle rectange = new Rectangle(length, breadth);

        // 先设置响应头,确保在获取输出流前完成
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "inline;filename=Marks_Ledger.pdf");
        
        // 仅获取一次输出流实例
        OutputStream out = response.getOutputStream();
        Document document = new Document(rectange);
        PdfWriter.getInstance(document, out);
        document.open();

        //other codes
        document.add(table);

        // 关闭document,自动刷新并关闭关联的输出流
        document.close();
        
        FacesContext.getCurrentInstance().responseComplete();
    } catch (DocumentException | IOException | RuntimeException e) {
        log.error("An error occurred during PDF generation:", e);
    }
}

修改说明

  • 响应头设置提前到获取输出流之前,确保客户端能正确识别文件类型和下载参数。
  • 只获取一次response.getOutputStream()并绑定到PdfWriter,避免流实例冲突。
  • 移除多余的try-with-resources块,依赖document.close()自动完成流的刷新与关闭,避免重复操作。

大文件场景额外优化建议

  • 如果能提前计算PDF的字节大小,添加response.setContentLength(contentLength),帮助客户端显示准确的下载进度。
  • 使用缓冲流包装输出流提升写入效率:
    OutputStream bufferedOut = new BufferedOutputStream(response.getOutputStream());
    PdfWriter.getInstance(document, bufferedOut);
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:14:54