При объединении Crystal Reports с itext7 в MVC создается поток PDF без потоков - PullRequest
0 голосов
/ 08 марта 2020

Я пытаюсь объединить два (или более) Crystal Reports в проект ASP. net MVC, и я загрузил пакет itext7 NuGet для этого. Я пытаюсь собрать простое доказательство концепции, в котором я объединяю 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.Merger(), и PdfDocument.CopyPagesTo(), и я думаю, что одного из них должно быть достаточно, чтобы выполнить работу самостоятельно? (И, конечно, я запустил код, пробуя каждый из них как по отдельности, так и вместе.) Но когда я запускаю приведенный выше код, загружаемый PDF-файл не объединяется, то есть отчет появляется только один раз. (Если я запускаю его с двумя разными отчетами, появляется только первый отчет.)

Теперь я возвращаю поток, пока делаю все интересные вещи с PdfMerger и PdfDocument объекты, так что для меня имеет смысл, что поток будет неизменным. Но все примеры использования iText 7, которые я нашел, возвращают либо поток, либо байтовый массив (например, этот вопрос StackOverflow ), так что, похоже, именно так он и должен работать.

Любые изменения, внесенные в код, либо не влияют, либо выдают ошибку, либо приводят к тому, что загруженный файл не читается браузером (т.е. не распознается как PDF). Например, я попытался преобразовать поток в байтовый массив и вернуть:

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

, но браузер не смог открыть загрузку. То же самое произошло, когда я попытался закрыть PdfDocument перед возвратом потока (пытаясь заставить его записать слияние в поток).

1 Ответ

1 голос
/ 08 марта 2020

В вашем коде много путаницы с потоками. Обычно поток используется либо для ввода, либо для вывода. MemoryStream может использоваться для обоих, но вы должны убедиться, что не закрыли его, чтобы иметь возможность использовать его снова. Часто проще и чище создать новый экземпляр с нижележащими байтами, чем повторное использование существующих, особенно с учетом того, что это не сильно влияет на производительность, так как базовые структуры тяжелых массивов все равно будут повторно использоваться новыми экземплярами. Вот пример того, как вы различаете guish между потоками. 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");
...