Процесс зависает при попытке прочитать байты из буфера в int. Wkhtmltopdf - PullRequest
0 голосов
/ 24 октября 2019

Мой метод создает PDF из содержимого HTML с помощью программы Wkhtmltopdf с Process. Я не могу найти причину, почему он зависает. Когда есть большая строка с HTML, я предполагаю, что она зависает, когда есть 30 страниц или больше. Но все работает нормально, если страниц меньше. Процесс Wkhtmltopdf.exe, который я вижу на Task Manager, не завершается вечно. Когда я останавливаю свой проект MVC с hanged Wkhtmltopdf, it creates normally PDF, как будто он чего-то ждет ... Вручную, конечно, Wkhtmltopdf создает все без проблем. Это не дубликат этого поста . Здесь у меня проблемы при попытке прочитать байтов в int.

public IActionResult createPdf()
{
    string html = "content";

    Process p;
    ProcessStartInfo psi = new ProcessStartInfo();

    psi.FileName = "...\\wkhtmltopdf.exe";
    psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";

    psi.UseShellExecute = false;
    psi.CreateNoWindow = true;
    psi.RedirectStandardInput = true;
    psi.RedirectStandardOutput = true;
    psi.RedirectStandardError = true;
    psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
    psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";

    p = Process.Start(psi);

    byte[] pdf = null;

    try
    {
        // Get PDF as bytes without temp files
        using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
        {
              stdin.AutoFlush = true;
              stdin.Write(html);
         }

         byte[] buffer = new byte[32768];
         using(var ms = new MemoryStream())
         {
              while(true)
              {
                   // HANGS HERE!!!
                   int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length); 

                   if(read <= 0)
                   {
                       break;
                   }       
                   ms.Write(buffer, 0, read);
              }
              pdf = ms.ToArray();
          }      
    }
    ...


}

1 Ответ

0 голосов
/ 31 октября 2019

Мне удалось решить эту проблему путем объединения разделенных pdf файлов в один файл с использованием PdfSharp вместо передачи объединенного содержимого в wkhtmltopdf как единое целое. Это длится немного дольше, но по крайней мере это работает. За исключением библиотеки PdfSharp (если вы используете MVC как меня, то PdfSharp.Core для исключения предупреждений о версии), вероятно, вам потребуется установить System.Text.Encoding.CodePages, чтобы исключить ошибку кодирования, которую может вызвать PdfSharp. Мой метод не использует временные файлы и работает только с байтами, в конце он отправляет созданный PDF в браузер.

using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;

public IActionResult createPdf()
{
    string html = "";
    byte[] pdf = null;

    using(PdfDocument doc = new PdfDocument())
    {
        for(int i = 0; i < files.Length; i++)
        {
            html = "html content";

            Process p;
            ProcessStartInfo psi = new ProcessStartInfo();

            psi.FileName = "...\\wkhtmltopdf.exe";
            psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";

            psi.UseShellExecute = false;
            psi.CreateNoWindow = true;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardError = true;
            psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
            psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";

            p = Process.Start(psi);

            using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
            {
                stdin.AutoFlush = true;
                stdin.Write(html);
            }

            byte[] buffer = new byte[32768];
            byte[] currentPdf = null;
            using(var ms = new MemoryStream())
            {
                while(true)
                {
                    int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length); 

                    if(read <= 0)
                    {
                        break;
                    }       
                    ms.Write(buffer, 0, read);
                 }
                 currentPdf = ms.ToArray();
             }

             p.StandardOutput.Close();
             p.WaitForExit(10000);
             p.Close();     

             MemoryStream currentPDF = new MemoryStream(currentPdf);

             // Merge separated pdfs into one
// Solves encoding errors   
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);

             using(PdfDocument pdfDoc = PdfReader.Open(currentPDF, PdfDocumentOpenMode.Import))
             {
                 for(int j = 0; j < pdfDoc.PageCount; j++)
                 {
                     doc.AddPage(pdfDoc.Pages[j]);
                 }
             }

             currentPDF.Close();
        }

        // Get merged pdfs as bytes
        MemoryStream rms = new MemoryStream();
        doc.Save(rms, false);
        pdf = rms.ToArray();
        rms.Close();
    }

    MemoryStream PDF = new MemoryStream(pdf);

    // Return PDF to browser
    return new FileStreamResult(PDF, "application/x-msdownload")
    {
        FileDownloadName = "mergedPdfs.pdf"
    }; 
}
...