C# 将Crystal报表与MVC中的itext7合并会从流中生成未合并的pdf

C# 将Crystal报表与MVC中的itext7合并会从流中生成未合并的pdf,c#,asp.net-mvc,pdf,crystal-reports,itext7,C#,Asp.net Mvc,Pdf,Crystal Reports,Itext7,我正在尝试在ASP.NETMVC项目中合并两个(或更多)Crystal报表,为此我下载了ITEXT7Nuget包。我正试图将一个简单的概念证明放在一起,在这个概念证明中,我用一个方法将一个pdf与它自身连接起来: var rpt1 = new CrystalDecisions.CrystalReports.Engine.ReportDocument(); var rpt2 = new CrystalDecisions.CrystalReports.Engine.ReportDocument();

我正在尝试在ASP.NETMVC项目中合并两个(或更多)Crystal报表,为此我下载了ITEXT7Nuget包。我正试图将一个简单的概念证明放在一起,在这个概念证明中,我用一个方法将一个pdf与它自身连接起来:

var rpt1 = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
var rpt2 = new CrystalDecisions.CrystalReports.Engine.ReportDocument();

rpt1.Load(Server.MapPath("~/Reports/MyReport.rpt");
rpt2.Load(Server.MapPath("~/Reports/MyReport.rpt");

DataTable table = GetDataMethod();
rpt1.SetDataSource(table);
rpt2.SetDataSource(table);

Stream stream = rpt.ExportToStream(ExportFormatType.PortableDocFormat);
var write = new PdfWriter(stream);
var doc = new PdfDocument(write);
var merger = new PdfMerger(doc);

var doc1 = new PdfDocument(new PdfReader(rpt1.ExportToStream(ExportFormatType.PortableDocFormat)));
var doc2 = new PdfDocument(new PdfReader(rpt2.ExportToStream(ExportFormatType.PortableDocFormat)));

merger.Merge(doc1, 1, doc1.GetNumberOfPages());
merger.Merge(doc2, 1, doc2.GetNumberOfPages());
doc.CopyPagesTo(1, doc2.GetNumberOfPages(), doc2);

stream.Flush();
stream.Position = 0;
return this.File(stream, "application/pdf", "DownloadName.pdf");
你可以看到我把所有的东西都扔到墙上,看看我在使用
PdfMerger.merge()
PdfDocument.CopyPagesTo()
,我认为这两个工具中的任何一个都足以独立完成这项工作了?(当然,我运行了这些代码,并将它们单独或一起进行了尝试。)但当我运行上述代码时,下载的PDF文件是未合并的,即报告只显示一次。(如果使用两个不同的报告运行,则只会显示第一个报告。)

现在,我正在返回流,同时我正在使用
PdfMerger
PdfDocument
对象执行所有有趣的操作,因此流保持不变对我来说是有意义的。但是我发现所有使用iText 7的示例都返回流或字节数组(例如),所以这似乎是它应该工作的方式

我对代码所做的任何更改要么无效,要么引发错误,要么导致浏览器无法读取下载的文件(即无法识别为PDF)。例如,我尝试将流转换为字节数组并返回:

using (var ms = new MemoryStream()) {
     stream.CopyTo(ms);
     byte[] bytes = ms.ToArray();
     return new FileContentResult(bytes, "application/pdf");
}

但当时浏览器无法打开下载。当我试图在返回流之前关闭
PdfDocument
时,也发生了同样的事情(试图强制它将合并写入流)

代码中的流有很多混乱之处。通常,流用于输入或输出
MemoryStream
可用于这两种情况,但您需要确保不要关闭它,以便能够重用它。使用底层字节创建新实例通常比重用现有字节更简单、更干净,特别是考虑到它不会对性能产生太大影响,因为底层重数组结构无论如何都会被新实例重用。下面是一个如何区分流的示例
ExportToStream
返回一个流,您可以从中获取包含PDF文件字节的字节数组,然后将这些文档加载到iText中,并创建第三个文档,将两个源文档合并到其中。然后,您必须确保调用
PdfDocument#Close()
,让iText完成您的文档,然后您可以获取合并文档的结果字节并传递它们,必要时将它们包装成流

var rpt1 = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
var rpt2 = new CrystalDecisions.CrystalReports.Engine.ReportDocument();

rpt1.Load(Server.MapPath("~/Reports/MyReport.rpt");
rpt2.Load(Server.MapPath("~/Reports/MyReport.rpt");

DataTable table = GetDataMethod();
rpt1.SetDataSource(table);
rpt2.SetDataSource(table);

var report1Stream = (MemoryStream)rpt1.ExportToStream(ExportFormatType.PortableDocFormat);
var report2Stream = (MemoryStream)rpt2.ExportToStream(ExportFormatType.PortableDocFormat);

var doc1 = new PdfDocument(new PdfReader(new MemoryStream(report1Stream.ToArray())));
var doc2 = new PdfDocument(new PdfReader(new MemoryStream(report2Stream.ToArray())));

var outStream = new MemoryStream();
var write = new PdfWriter(outStream);
var doc = new PdfDocument(write);
var merger = new PdfMerger(doc);

merger.Merge(doc1, 1, doc1.GetNumberOfPages());
merger.Merge(doc2, 1, doc2.GetNumberOfPages());

doc.Close();
doc1.Close();
doc2.Close();

return this.File(new MemoryStream(outStream.ToArray()), "application/pdf", "DownloadName.pdf");

不幸的是,Crystal Report的ExportToStream方法不能直接强制转换到MemoryStream而不抛出错误(无论如何,从2008年开始)。我必须实例化两个新的MemoryStream,然后使用Stream.CopyTo()来填充它们。这就是说,有了这个小小的变化,这一切都完美地实现了!非常感谢你的帮助。