Медленное сохранение PDF-файла после 10 000 страниц в iTextSharp - PullRequest
0 голосов
/ 28 февраля 2019

Я пытаюсь создать приложение, которое создает PDF и сохраняет в локальном файле.Я использую это через iTextSharp в C #.Мне нужно сохранить как минимум 100 000 файлов PDF.

После 10 000 файлов сохранение замедляется.Первые 10 КБ сохраняются за 2 минуты, остальные файлы занимают почти 5 часов.

Я считаю, что проблема заключается в использовании памяти.Но я не мог найти решение, чтобы решить эту проблему.Я разместил исходный код.

private void CreatePDF11()
{
    Queue<String> QTempFileNames;
    QTempFileNames = new Queue<string>();
    string fileName = string.Empty;

    DateTime fileCreationDatetime = DateTime.Now;

    fileName = @"D:\StatementMassPrint\tesystw14j414435.PDF";

    string DestinationDirectory = @"D:\StatementMassPrint";

    string StrTempFilePath = String.Empty;

    string pdfPath = fileName;

    int TempPdfFileName = 1;
    if (DestinationDirectory != string.Empty)
    {
        StrTempFilePath = DestinationDirectory + "temp" + "(" + DateTime.Now.ToFileTime() + ")";
    }

    if (!(Directory.Exists(StrTempFilePath)))
    {
        Directory.CreateDirectory(StrTempFilePath);
    }
        string reportFileName = StrTempFilePath + "\\" + TempPdfFileName.ToString() + ".Pdf";

        for (int f = 0; f < 100000; f++)
        {
            reportFileName = StrTempFilePath + "\\" + TempPdfFileName.ToString() + ".Pdf";

            using (FileStream msReport = new FileStream(reportFileName, FileMode.Create))
    {
        //step 1
        using (Document pdfDoc = new Document(PageSize.A5.Rotate(), 10f, 10f, 200f,40f))
        {
            try
            {
                // step 2
                PdfWriter pdfWriter = PdfWriter.GetInstance(pdfDoc, msReport);
                pdfWriter.PageEvent = new EpisodePageHeaderAndFooter();

                //open the stream 
                pdfDoc.Open();

                DataTable dtEpisodeWise = new DataTable();
                dtEpisodeWise.Columns.Add("INVOICE_NO");
                dtEpisodeWise.Columns.Add("INVOICE_DATE");
                dtEpisodeWise.Columns.Add("CODE");
                dtEpisodeWise.Columns.Add("SERVICE_DESCRIPTION");
                dtEpisodeWise.Columns.Add("QTY",typeof(decimal));
                dtEpisodeWise.Columns.Add("UNIT_PRICE",typeof(decimal));
                dtEpisodeWise.Columns.Add("GROSS",typeof(decimal));
                dtEpisodeWise.Columns.Add("DISCOUNT",typeof(decimal));
                dtEpisodeWise.Columns.Add("NET",typeof(decimal));
                dtEpisodeWise.Columns.Add("DEDUCTION",typeof(decimal));
                dtEpisodeWise.Columns.Add("NET_PAYABLE_WITHOUT_VAT",typeof(decimal));
                dtEpisodeWise.Columns.Add("VAT",typeof(decimal));
                dtEpisodeWise.Columns.Add("NET_PAYABLE_WITH_VAT",typeof(decimal));

                PdfPTable table = new PdfPTable(dtEpisodeWise.Columns.Count);
                table.WidthPercentage = 100;

                Font fontH1 = new Font(Font.FontFamily.HELVETICA, 6, Font.BOLDITALIC);
                for (int k = 0; k < dtEpisodeWise.Columns.Count; k++)
                {

                    string str = System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(dtEpisodeWise.Columns[k].ColumnName.Replace("_", " ").ToLower()); ;
                    PdfPCell cell = new PdfPCell(new Phrase(str,fontH1));
                    cell.HorizontalAlignment = PdfPCell.ALIGN_CENTER;
                    cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
                    //    cell.BackgroundColor = new iTextSharp.text.BaseColor(51, 102, 102);

                    table.AddCell(cell);
                }

             //   for (int i = 0; i < 1000; i++)
             //   {
                    dtEpisodeWise.Rows.Add("CR100005", "25-05-1989", "CPT004", "SERVICE005", 1, 10, 100, 10, 90, 45, 45, 5, 50);
                    dtEpisodeWise.Rows.Add("CR100006", "25-05-1992", "CPT00555", "SERVICE105",6, 60, 600, 60, 450, 45, 45, 5, 500);

             //   }

                Font fontH2 = new Font(Font.FontFamily.HELVETICA, 6, Font.ITALIC);

                for (int i = 0; i < dtEpisodeWise.Rows.Count; i++)
                {
                    for (int j = 0; j < dtEpisodeWise.Columns.Count; j++)
                    {

                        PdfPCell cell = new PdfPCell(new Phrase(dtEpisodeWise.Rows[i][j].ToString(),fontH2));

                        //Align the cell in the center

                        if (dtEpisodeWise.Columns[j].DataType == typeof(decimal))
                        {
                            cell.HorizontalAlignment = PdfPCell.ALIGN_RIGHT;
                            cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
                        }
                        else
                        {
                            cell.HorizontalAlignment = PdfPCell.ALIGN_LEFT;
                            cell.VerticalAlignment = PdfPCell.ALIGN_CENTER;
                        }

                        table.HeaderRows = 1;

                        table.AddCell(cell);
                    }
                }
                    pdfDoc.Add(table);

                pdfDoc.Close();
                TempPdfFileName++;



            }
            catch (Exception ex)
            {
                //handle exception
            }
            finally
            {

            }
        }
    }
}
}

1 Ответ

0 голосов
/ 28 февраля 2019

Ну, есть несколько вещей, которыми вы еще не избавились, которые могут вызвать проблему.Во-первых, DataTable реализует IDisposable, как и PdfWriter.

. Для PdfWriter вам не нужно это как объявление переменной, так как вы на самом деле его не используетев любом случае, вы можете просто обработать это как

using (PdfWriter.GetInstance(pdfDoc, msReport))
{
    // ...
}

. Стоит заметить, что вы в настоящее время постоянно воссоздаете шрифты fontH1 и fontH2 внутри цикла for.Я на самом деле не вижу необходимости, они не меняются в течение всего запуска вашей программы, поэтому вы можете объявить их вне вашего цикла как локальные переменные или как часть статического поля внутри вашего класса, создавая PDF.

Другое дело, что вы, кажется, воссоздаете один и тот же объект данных снова и снова, теперь я могу предположить, что для вашего текущего кода это просто фиктивный код, но, как я вижу, он вам не нуженчтобы создать datatable внутри вашего цикла, я скорее думаю, что это может быть аргументом для создания вашего pdf файла (как и имени файла), так что вы можете переписать свой код для создания pdf в его собственном классе (я назвал его PdfModuleно я держу пари, что для него есть намного лучшие названия :)), и реструктурирую ваш код для обработки 1 файла за один раз, например, так:

public class PdfModule
{
    private static readonly Font H1Font = new Font(Font.FontFamily.HELVETICA, 6, Font.BOLDITALIC);
    private static readonly Font H2Font = new Font(Font.FontFamily.HELVETICA, 6, Font.ITALIC);

    public static void CreateFile(string filename, DataTable data)
    {
        using (var msReport = new FileStream(filename, FileMode.Create, FileAccess.Write))
        {
            using (var pdfDoc = new Document(PageSize.A5.Rotate(), 10f, 10f, 200f, 40f))
            {
                using (PdfWriter.GetInstance(pdfDoc, msReport))
                {
                    pdfDoc.Open();


                    var table = new PdfPTable(data.Columns.Count)
                    {
                        WidthPercentage = 100,
                        HeaderRows = 1
                    };
                    for (var k = 0; k < data.Columns.Count; k++)
                    {
                        var str =
                            System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(data
                                .Columns[k].ColumnName.Replace("_", " ").ToLower());
                        ;
                        var cell = new PdfPCell(new Phrase(str, H1Font))
                        {
                            HorizontalAlignment = Element.ALIGN_CENTER,
                            VerticalAlignment = Element.ALIGN_CENTER
                        };

                        table.AddCell(cell);
                    }

                    for (var i = 0; i < data.Rows.Count; i++)
                    {
                        for (var j = 0; j < data.Columns.Count; j++)
                        {
                            var cell =
                                new PdfPCell(new Phrase(data.Rows[i][j].ToString(), H2Font))
                                {
                                    VerticalAlignment = Element.ALIGN_CENTER,
                                    HorizontalAlignment = data.Columns[j].DataType == typeof(decimal)
                                        ? Element.ALIGN_RIGHT
                                        : Element.ALIGN_LEFT
                                };

                            table.AddCell(cell);
                        }
                    }

                    pdfDoc.Add(table);

                    pdfDoc.Close();
                }
            }
        }
   }
}

Это сохранит объявление шрифта как статическоеполя на уровне класса, поэтому инициализируем их только один раз во время выполнения программ, и записываем 1 отдельный файл с 1 одиночным DataTable строительствомдо документа.Оба посылаются в качестве аргументов.

Чтобы затем использовать этот класс, я написал следующий пример кода, и он, похоже, генерирует 100 000 файлов довольно быстро (не где-то рядом с 5 часами, которые вы упомянули).

Обратите внимание, что у меня нет подробной информации о том, как вы обрабатываете / заполняете данные в вашей реальной программе, но, по крайней мере, это даст вам основную идею о том, как реструктурировать ваш код, и перейдите оттуда

internal class Program
{
    private static DataTable CreateDataTable( IEnumerable<object[]> rawData )
    {
        var datatable = new DataTable();
        datatable.Columns.Add("INVOICE_NO");
        datatable.Columns.Add("INVOICE_DATE");
        datatable.Columns.Add("CODE");
        datatable.Columns.Add("SERVICE_DESCRIPTION");
        datatable.Columns.Add("QTY", typeof(decimal));
        datatable.Columns.Add("UNIT_PRICE", typeof(decimal));
        datatable.Columns.Add("GROSS", typeof(decimal));
        datatable.Columns.Add("DISCOUNT", typeof(decimal));
        datatable.Columns.Add("NET", typeof(decimal));
        datatable.Columns.Add("DEDUCTION", typeof(decimal));
        datatable.Columns.Add("NET_PAYABLE_WITHOUT_VAT", typeof(decimal));
        datatable.Columns.Add("VAT", typeof(decimal));
        datatable.Columns.Add("NET_PAYABLE_WITH_VAT", typeof(decimal));


        foreach (var row in rawData)
        {
            datatable.Rows.Add(row);
        }

        return datatable;
    }

    public static void Main(string[] args)
    {
        var rowData = new List<object[]>()
        {
            new object[] { "CR100005", "25-05-1989", "CPT004", "SERVICE005", 1, 10, 100, 10, 90,
                45, 45, 5, 50 },
            new object[] { "CR100006", "25-05-1992", "CPT00555", "SERVICE105", 6, 60, 600, 60,
            450, 45, 45, 5, 500 }
        };

        var pdfModule = new PdfModule();
        var outDirectory = Path.Combine(Environment.CurrentDirectory, "Output");
        if (!Directory.Exists(outDirectory))
        {
            // well theoretically I should just create the directory and worry about conflicts differently
            Directory.CreateDirectory(outDirectory);
        }

        Console.WriteLine( $"Creating files to {outDirectory}");
        var nrOfFiles = 100000;
        var stepCount = 1000;
        for (var i = 0; i < nrOfFiles; i++)
        {
            if (i % stepCount == 0)
            {
                Console.WriteLine($"Creating files {i}-{i+stepCount-1}" );
            }
            var filename = Path.Combine(outDirectory, $"{i}.pdf");
            using (var dataTable = CreateDataTable(rowData))
            {
                pdfModule.CreateFile(filename, dataTable);
            }
        }
        Console.WriteLine($"Done, created {nrOfFiles} files");
    }
}

В остальном я не уверен, что моя среда совпадает с вашей, но она работала согласованно (вызывая уведомления ITextSharp, что у меня нет действующей лицензии AGPL ^ _ ^), и я ее запускалпод Linux с помощью Rider IDE

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...