C#为多个MemoryStream添加PDF加密:无法访问已关闭流
问题分析与解决方案
你的核心问题在于同时混用了两种PDF操作方式(PdfWriter+Document 和 PdfStamper),并且错误地在循环中过早关闭了目标流,导致要么流被关闭报错,要么加密逻辑没生效。
具体问题点
- 你在循环里用
PdfStamper处理每个PDF片段时,pdfStamper.Close()会直接关闭底层的arrayMemoryStream,这就导致后续循环迭代时无法再写入这个流——所以你注释掉它才不会报错,但这样PdfStamper的加密逻辑也不会执行(加密操作是在Close时完成的)。 - 用
Document+PdfWriter手动添加页面的方式,和PdfStamper的功能重叠,两者混用会破坏PDF的结构,导致加密失效或者文件损坏。
修正后的代码
我们可以调整思路:先把所有PDF片段合并成一个未加密的MemoryStream,然后再对整个合并后的PDF进行加密处理,这样就不会出现流冲突的问题:
public MemoryStream CreateMemoryStream() { // 生成动态PDF片段 byte[] dynamicPdfBytes; using (var dynamicPDfMemoryStream = new MemoryStream()) { using (var document = new Document(PageSize.A4, 25, 25, 30, 30)) { PdfWriter.GetInstance(document, dynamicPDfMemoryStream); document.Open(); var pdfPTable = new PdfPTable(1) { TotalWidth = _totalWidth, LockedWidth = true, SpacingBefore = _spacingBefore, SpacingAfter = _spacingAfter }; float[] widths = { 272f }; pdfPTable.SetWidths(widths); var image = Image.GetInstance(HttpContext.Current.Server.MapPath("/Images/logo.png")); image.ScaleToFit(125f, 125f); image.Alignment = Image.RIGHT_ALIGN; var pdfPCell = new PdfPCell(image) { Border = 0, HorizontalAlignment = Element.ALIGN_RIGHT }; pdfPTable.AddCell(pdfPCell); document.Add(pdfPTable); } dynamicPdfBytes = dynamicPDfMemoryStream.ToArray(); } // 处理带表单的PDF并扁平化 byte[] pdfBytes; using (var pdfReader = new PdfReader(HttpContext.Current.Server.MapPath("/Documents/Test.pdf"))) { using (var pdfMemoryStream = new MemoryStream()) { var pdfStamper = new PdfStamper(pdfReader, pdfMemoryStream); var acroFields = pdfStamper.AcroFields; acroFields.SetField("TestField", "This is a test"); pdfStamper.FormFlattening = true; pdfStamper.Close(); pdfBytes = pdfMemoryStream.ToArray(); } } var files = new List<byte[]> { dynamicPdfBytes, pdfBytes }; // 第一步:合并所有PDF片段到一个临时流 byte[] mergedBytes; using (var mergedStream = new MemoryStream()) { using (var document = new Document(PageSize.A4, 25, 25, 30, 30)) { var pdfCopy = new PdfCopy(document, mergedStream); document.Open(); foreach (var bytes in files) { using (var reader = new PdfReader(bytes)) { for (int i = 1; i <= reader.NumberOfPages; i++) { pdfCopy.AddPage(pdfCopy.GetImportedPage(reader, i)); } } } } mergedBytes = mergedStream.ToArray(); } // 第二步:对合并后的PDF进行加密 using (var encryptedStream = new MemoryStream()) { using (var reader = new PdfReader(mergedBytes)) { var stamper = new PdfStamper(reader, encryptedStream); // 设置加密:空用户密码,指定所有者密码,允许打印权限,128位加密 stamper.SetEncryption(null, _password, PdfWriter.ALLOW_PRINTING, PdfWriter.STANDARD_ENCRYPTION_128); stamper.Close(); } // 重置流位置并返回 encryptedStream.Position = 0; return encryptedStream; } }
关键改进点
- 分离合并与加密步骤:先使用
PdfCopy安全合并所有PDF片段,再对整个合并后的文件进行加密,避免流操作冲突。 - 正确管理流生命周期:每个
PdfReader和PdfStamper都用using语句包裹,确保资源正确释放,同时不会提前关闭目标流。 - 使用
PdfCopy替代手动添加页面:PdfCopy是iText中专门用于合并PDF的类,比手动用DirectContent.AddTemplate更可靠,能保证PDF结构完整。
内容的提问来源于stack exchange,提问作者iggyweb
相关产品推荐
相关产品推荐

