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

使用iText7生成PDF时,如何实时检查文件大小并自动拆分大文件

iText7生成PDF时,如何实时检查文件大小并自动拆分大文件

我明白你遇到的痛点了——iText7的内部缓冲机制确实会让你没法实时拿到内存流的真实大小,直到文档完全关闭才会把所有数据刷进去,之前依赖的Flush()方法还被废弃了。咱们换个思路解决:自定义一个能实时统计写入字节数的输出流,绕过iText的缓冲限制,随时掌握已生成内容的大小。

第一步:实现计数输出流

写一个CountingOutputStream类,它包装原有的MemoryStream,每次写入数据时实时累加字节数,这样不管iText有没有缓冲,我们都能拿到准确的已写入字节量。

public class CountingOutputStream : Stream
{
    private readonly Stream _innerStream;
    private long _bytesWritten;

    public CountingOutputStream(Stream innerStream)
    {
        _innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
        _bytesWritten = 0;
    }

    public long BytesWritten => _bytesWritten;

    // 拆分文件时重置计数
    public void ResetCount()
    {
        _bytesWritten = 0;
    }

    // 实现Stream的抽象方法
    public override bool CanRead => _innerStream.CanRead;
    public override bool CanSeek => _innerStream.CanSeek;
    public override bool CanWrite => _innerStream.CanWrite;
    public override long Length => _innerStream.Length;

    public override long Position
    {
        get => _innerStream.Position;
        set => _innerStream.Position = value;
    }

    public override void Flush() => _innerStream.Flush();

    public override int Read(byte[] buffer, int offset, int count) => _innerStream.Read(buffer, offset, count);

    public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);

    public override void SetLength(long value) => _innerStream.SetLength(value);

    public override void Write(byte[] buffer, int offset, int count)
    {
        _innerStream.Write(buffer, offset, count);
        _bytesWritten += count;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _innerStream.Dispose();
        }
        base.Dispose(disposing);
    }
}

第二步:修改主逻辑代码

用这个自定义流替代直接使用MemoryStream,每次添加内容后检查BytesWritten是否超过阈值。重点:拆分文件前一定要先关闭当前的Document和PdfDocument,这样iText才会把所有内部缓冲的数据写入计数流,确保计数和文件内容都准确。

修改后的代码示例:

const long MaxFileSize = 20 * 1024 * 1024; // 20MB阈值
int pdfIndex = 0;
string currentPdfFileName = GetPdfFileName(pdfFileName, pdfIndex);

using (MemoryStream memoryStream = new MemoryStream())
using (CountingOutputStream countingStream = new CountingOutputStream(memoryStream))
{
    PdfWriter writer = new PdfWriter(countingStream);
    writer.SetCloseStream(false);
    PdfDocument pdf = new PdfDocument(writer);
    Document document = new Document(pdf);
    int pageCount = 1;
    int imageIndex = 0;

    while (imageIndex < imageFiles.Count)
    {
        // ... 这里写你添加内容的逻辑(比如插入图片、文字等) ...

        // 检查当前已写入字节数是否超过阈值
        if (countingStream.BytesWritten >= MaxFileSize)
        {
            // 关闭当前文档,确保所有缓冲数据写入流
            document.Close();
            pdf.Close();

            // 保存当前PDF文件
            SavePdfToFile(memoryStream, currentPdfFileName);

            // 重置流和计数,准备生成新的PDF
            memoryStream.SetLength(0);
            memoryStream.Position = 0;
            countingStream.ResetCount();

            // 初始化新的PDF文档对象
            pdfIndex++;
            currentPdfFileName = GetPdfFileName(pdfFileName, pdfIndex);
            writer = new PdfWriter(countingStream);
            writer.SetCloseStream(false);
            pdf = new PdfDocument(writer);
            document = new Document(pdf);
            pageCount = 1;
        }

        imageIndex++;
    }

    // 保存最后一份未达阈值的PDF文件
    document.Close();
    pdf.Close();
    SavePdfToFile(memoryStream, currentPdfFileName);
}

关键细节说明

  • 自定义流的作用:iText的PdfWriter会把数据写入我们的计数流,不管内部有没有缓冲,只要它往流里写了数据,我们就实时计数,彻底解决了无法实时获取大小的问题。
  • 必须关闭旧文档:如果不关闭Document和PdfDocument,iText会把部分数据留在内部缓冲区,既会导致计数不准,保存的文件也会不完整。
  • 重置流和计数:拆分后要清空内存流并重置位置,同时重置计数流的字节数,确保新文档从0开始统计。

这样就能实现实时检查PDF大小、超过阈值自动拆分的需求了!

备注:内容来源于stack exchange,提问作者Donatello Mecca

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 11:27:57