使用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
相关产品推荐
相关产品推荐

