使用DinkToPdf库将html转换为PDF的处理需要花费大量时间

时间:2019-02-21 14:44:53

标签: c# asp.net-core asp.net-core-2.0 html-to-pdf dinktopdf

运行以下代码行时,使用DinkToPdf库将html转换为PDF的过程需要很长的时间:ConverterToPdf.Convert(pdf)

我该如何解决这个问题?

public byte[] ConvertReportToPDFAsync<TViewModel>() where TViewModel : new()
{
   var globalSettings = new GlobalSettings
   {
      ColorMode = ColorMode.Color,
      Orientation = Orientation.Portrait,
      PaperSize = PaperKind.A4,
      Margins = new MarginSettings { Top = 10 },
      DocumentTitle = documentTitle,
   };

   var objectSettings = new ObjectSettings
   {
      PagesCount = true,
      HtmlContent =@"<html><body><div>Hello</div></body></html>",
      WebSettings = { DefaultEncoding = "utf -8", UserStyleSheet = Path.Combine(Directory.GetCurrentDirectory(), "assets", "PruefReportDataTableFormat.css") },
      HeaderSettings = { FontName = "Arial", FontSize = 9, Right = "Page [page] of [toPage]", Line = true },
      FooterSettings = { FontName = "Arial", FontSize = 9, Line = true, Center = "Report Footer" }
    };
    var pdf = new HtmlToPdfDocument()
    {
       GlobalSettings = globalSettings,
       Objects = { objectSettings }
    };

   byte[] file = ConverterToPdf.Convert(pdf); // TOO LONG PROCESS !!!!

   return file;

}

启动课程:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpContextAccessor(); 
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddMemoryCache(); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();

        services.AddSingleton(typeof(IConverter), new SynchronizedConverter(new PdfTools())); // DinkToPdf
    }
}
  

已更新:

跟踪后,我发现问题出在 WkHtmlToXBindings.wkhtmltopdf_convert 方法 namespace DinkToPdf

namespace DinkToPdf
{
   public sealed class PdfTools : ITools, IDisposable
   {
      // some codes
      public bool DoConversion(IntPtr converter)
      {
         return WkHtmlToXBindings.wkhtmltopdf_convert(converter); // ACTUALLY TOO LONG PROCESS OCCURS HERE !!!!
      }
   }
}
  

更新2:

深入研究之后,我发现在这行代码中, Monitor.Wait((object) task1); 是一个漫长的过程! (我无法更改此代码,因为该代码是dinktopdf.dll文件的一部分。)

namespace DinkToPdf
{
   public class SynchronizedConverter : BasicConverter
   {
      public TResult Invoke<TResult>(Func<TResult> @delegate)
      {
         this.StartThread();
         Task<TResult> task1 = new Task<TResult>(@delegate);
         Task<TResult> task2 = task1;
         bool lockTaken = false;
         try
         {
             Monitor.Enter((object) task2, ref lockTaken);
             this.conversions.Add((Task) task1);
             Monitor.Wait((object) task1);   //Bottleneck!! LONG PROCESS!!
         }
         finally
         {
            if (lockTaken)
               Monitor.Exit((object) task2);
         }
         if (task1.Exception != null)
            throw task1.Exception;
         return task1.Result;
      }
   }
}

2 个答案:

答案 0 :(得分:0)

我看不到您如何获得ConverterToPdf,但可以尝试以下操作:如果在服务中使用var converter = new SynchronizedConverter(new PdfTools());´, remove it, and just inject IConverter`初始化转换器。

我尝试过并且这种方法有效,毕竟,我们已经注册了转换器,我们不需要使用new关键字来创建实例。

答案 1 :(得分:0)

这是您在Startup.cs上创建的单例,因此首先需要执行的是用依赖项注入填充根对象。

private readonly IConverter _converter;

public YourControllerService(IConverter converter)
{            
    _converter = converter            
}

public void CreatePdf() {
    byte[] bytes = _converter.Convert(doc);
}

其次,请确保使用网络安全字体来加快渲染速度。在html内部使用来自外部url的字体会减慢该过程。例如,假设这是您的html模板,您有5个页面:

<html>
<head>
    <style>
        @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,700');
        body {
            font-family: 'Source Sans Pro', sans-serif;
        }
    <style>
<head>
</html>

如果您通过以下方式对此进行更改:

<html>
<head>
    <style>         
        body {
            font-family: 'Verdana'; 
        }
    <style>
<head>
</html>

您将看到速度差异。另外,根据我的经验,降低DPI也会影响渲染速度。

var doc = new HtmlToPdfDocument()
{
    GlobalSettings = {
        ColorMode = ColorMode.Color,
        Orientation = Orientation.Landscape,
        PaperSize = PaperKind.A4,
        DPI = 320,
        Out = path
    },
    Objects = {
        new ObjectSettings() {                               
            PagesCount = true,
            HtmlContent = html,
            WebSettings = { DefaultEncoding = "UTF-8", LoadImages = true },
            FooterSettings = { FontSize = 8, Right = "Page [page] of [toPage]", Line = false, Spacing = 2.812 }
        }
    }
};

byte[] bytes = _converter.Convert(doc);

希望有帮助。

相关问题