DinkToPdf
DinkToPdf copied to clipboard
Unable to create a pdf in Azure Function with .NET Core 2.0
Hello,
I use your library to create a pdf out of a template html file. This works great on my local dev environment and the Azure Functions emulator. But when I run the function in the real Functions service I get this error.
2018-10-31T14:03:40 Welcome, you are now connected to log-streaming service.
2018-10-31T14:04:03.540 [Information] Executing 'HtmlToPdf' (Reason='This function was programmatically called via the host APIs.', Id=94622287-91dc-4360-94d4-cae68def34d6)
2018-10-31T14:04:03.639 [Information] C# HTTP trigger function processed a request.
2018-10-31T14:04:03.640 [Information] Type $DinkToPdf.SynchronizedConverter
2018-10-31T14:04:03.640 [Information] Payload is 1096 bytes
2018-10-31T14:04:03.661 [Information] Getting paths
2018-10-31T14:04:03.661 [Information] bin-Dir is D:\home\site\wwwroot\bin
2018-10-31T14:04:14.629 [Information] wkhtmltopdf loaded from D:\home\site\wwwroot\bin\..\lib\x32\libwkhtmltox.dll
2018-10-31T14:04:14.629 [Information] Article list html template path is D:\home\site\wwwroot\bin\..\assets\articleList.html
2018-10-31T14:04:14.724 [Information] Html template loaded
2018-10-31T14:04:14.959 [Information] Generating pdf
2018-10-31T14:04:15.645 [Information] PhaseChanged: [0] Loading pages
2018-10-31T14:04:15.648 [Information] ProgressChanged: 0%
2018-10-31T14:04:15.653 [Information] ProgressChanged: 10%
2018-10-31T14:04:15.869 [Information] ProgressChanged: 22%
2018-10-31T14:05:40 No new trace in the past 1 min(s).
2018-10-31T14:06:40 No new trace in the past 2 min(s).
2018-10-31T14:07:40 No new trace in the past 3 min(s).
2018-10-31T14:07:40 No new trace in the past 4 min(s).
2018-10-31T14:09:03.590 [Error] Timeout value of 00:05:00 exceeded by function 'HtmlToPdf' (Id: '94622287-91dc-4360-94d4-cae68def34d6'). Initiating cancellation.
It always stops processing at 22%. A simplified code of my solution looks like this:
namespace Sample.Backend
{
public static class HtmlToPdf
{
private static readonly SynchronizedConverter PdfConverter = new SynchronizedConverter(new PdfTools());
[FunctionName("HtmlToPdf")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation($"Type ${PdfConverter.GetType().ToString()}");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Payload is {System.Text.Encoding.Unicode.GetByteCount(requestBody)} bytes");
log.LogInformation("Getting paths");
var currentAssembly = System.Reflection.Assembly.GetExecutingAssembly();
var location = currentAssembly.Location;
var binDir = Path.GetDirectoryName(location);
log.LogInformation($"bin-Dir is {binDir}");
var calc = new CustomAssemblyLoadContext();
var wkHtmlToPdfPath = $"{binDir}\\..\\lib\\x32\\libwkhtmltox.dll";
calc.LoadUnmanagedLibrary(wkHtmlToPdfPath);
log.LogInformation($"wkhtmltopdf loaded from {wkHtmlToPdfPath}");
log.LogInformation($"Building pdf");
var pdfBytes = BuildPdf("<html>...</html>", log);
log.LogInformation($"Pdf build");
var fileResponse = new FileContentResult(pdfBytes, "application/pdf");
return fileResponse;
}
private static byte[] BuildPdf(string html, ILogger log)
{
log.LogInformation("Generating pdf");
var htmlToPdfDoc = new HtmlToPdfDocument();
htmlToPdfDoc.Objects.Add(new ObjectSettings()
{
HtmlContent = html,
FooterSettings = { Right = "[page] / [toPage]", Line = true }
});
AppendEvents(PdfConverter, log);
var pdfAsBytes = PdfConverter.Convert(htmlToPdfDoc);
log.LogInformation("Pdf generated");
return pdfAsBytes;
}
private static void AppendEvents(SynchronizedConverter pdfConverter, ILogger log)
{
pdfConverter.ProgressChanged += (sender, args) =>
{
log.LogInformation($"ProgressChanged: {args.Description}");
};
pdfConverter.Error += (sender, args) =>
{
log.LogInformation($"Warning: {args.Message}");
};
pdfConverter.Finished += (sender, args) =>
{
log.LogInformation($"Finished: {args.Success}");
};
pdfConverter.PhaseChanged += (sender, args) =>
{
log.LogInformation($"PhaseChanged: [{args.CurrentPhase}] {args.Description}");
};
pdfConverter.Warning += (sender, args) =>
{
log.LogInformation($"Warning: {args.Message}");
};
}
}
public class CustomAssemblyLoadContext : AssemblyLoadContext
{
public IntPtr LoadUnmanagedLibrary(string absolutePath)
{
return LoadUnmanagedDll(absolutePath);
}
protected override IntPtr LoadUnmanagedDll(String unmanagedDllName)
{
return LoadUnmanagedDllFromPath(unmanagedDllName);
}
protected override Assembly Load(AssemblyName assemblyName)
{
throw new NotImplementedException();
}
}
}
Maybe you have a solution for my problem. Is there something I have to configure in my function?
I have the same problem, could you come up with any solution?
Same problem here... PDF halts to generate in Azure functions .NET Core 3.1 (Consumption plan). After 10 minutes runtime kills process. I've tried to switch azure function runtime to x64. - but didn't helped. Have anybody tried/test this successfully?
Same problem here... PDF halts to generate in Azure functions .NET Core 3.1 (Consumption plan). After 10 minutes runtime kills process. I've tried to switch azure function runtime to x64. - but didn't helped. Have anybody tried/test this successfully?
I will probably move to app service plan - not consumption. https://odetocode.com/blogs/scott/archive/2018/02/14/pdf-generation-in-azure-functions-v2.aspx
What to Worry About Notice the converter class has the name SynchronizedConverter. The word synchronized is a clue that the converter is single threaded. Although the library can buffer conversion requests until a thread is free to process those requests, it would be safer to trigger the function with a message queue to avoid losing conversion requests in case of a restart.
You should also know that the function will not execute successfully in a consumption plan. You’ll need to use a Basic or higher app service plan in Azure.