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

C#为多个MemoryStream添加PDF加密:无法访问已关闭流

问题分析与解决方案

你的核心问题在于同时混用了两种PDF操作方式(PdfWriter+Document 和 PdfStamper),并且错误地在循环中过早关闭了目标流,导致要么流被关闭报错,要么加密逻辑没生效。

具体问题点

  1. 你在循环里用PdfStamper处理每个PDF片段时,pdfStamper.Close()会直接关闭底层的arrayMemoryStream,这就导致后续循环迭代时无法再写入这个流——所以你注释掉它才不会报错,但这样PdfStamper的加密逻辑也不会执行(加密操作是在Close时完成的)。
  2. 用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 03:32:30