C# 将Crystal报表与MVC中的itext7合并会从流中生成未合并的pdf
我正在尝试在ASP.NETMVC项目中合并两个(或更多)Crystal报表,为此我下载了ITEXT7Nuget包。我正试图将一个简单的概念证明放在一起,在这个概念证明中,我用一个方法将一个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();
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()来填充它们。这就是说,有了这个小小的变化,这一切都完美地实现了!非常感谢你的帮助。