0

我遵循了本教程:https ://www.freecodecamp.org/news/convert-html-to-pdf-with-azure-functions-and-wkhtmltopdf/

解决方案是 Azure C# Function App,它将 HTML 转换为 PDF 文档并将该 PDF 保存在 blob 存储中。这个特定的功能应用程序接受以下内容,我通过 Postman 在本地运行时通过请求正文:

{
  "PDFFileName": "test.pdf",
  "HtmlContent": "<html>\n        <head><meta http-equiv=Content-Type content=\"text/html; charset=UTF-8\">\n        </head>\n        <body>\n            <div>test</div>\n        </body>    \n    </html>"
}

本教程使用这个库:https ://github.com/rdvojmoc/DinkToPdf

发生的事情有点难以调试。我在本地运行函数应用程序,它运行良好,并且 PDF 保存在我的 blob 存储中,没有任何问题,但是当它运行时,我确实在控制台中看到了下面的错误。

Qt: Could not initialize OLE (error 80010106)

然后当我再次运行它时(甚至删除 pdf,以确保那里没有冲突),应用程序只是挂起,直到它返回 503 或我取消请求。

跟踪代码,这是它发生故障的这一行(来自下面的代码):

var PDFByteArray = BuildPdf(Request.HtmlContent, new MarginSettings(2, 2, 2, 2));

直接在门户中发布和运行(测试 + 代码并在逻辑应用流中运行)时,我观察到相同的行为。

我拥有的应用服务计划是基本 (B1: 1)。任何解决此问题的帮助将不胜感激!

完整代码如下:

using System;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using DinkToPdf;
using IPdfConverter = DinkToPdf.Contracts.IConverter;

[assembly: FunctionsStartup(typeof(pdfCreation.Startup))]

namespace pdfCreation
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton(typeof(IPdfConverter), new SynchronizedConverter(new PdfTools()));            
        }
    }

    public class Html2Pdf
    {
        // Read more about converter on: https://github.com/rdvojmoc/DinkToPdf
        // For our purposes we are going to use SynchronizedConverter
        IPdfConverter pdfConverter = new SynchronizedConverter(new PdfTools());

        // A function to convert html content to pdf based on the configuration passed as arguments
        // Arguments:
        // HtmlContent: the html content to be converted
        // Margins: the margis around the content
        // DPI: The dpi is very important when you want to print the pdf.
        // Returns a byte array of the pdf which can be stored as a file
        private byte[] BuildPdf(string HtmlContent, MarginSettings Margins, int? DPI = 180)
        {
            // Call the Convert method of SynchronizedConverter "pdfConverter"
            return pdfConverter.Convert(new HtmlToPdfDocument()
            {
                // Set the html content
                Objects =
                {
                    new ObjectSettings
                    {
                        HtmlContent = HtmlContent
                    }
                },
                // Set the configurations
                GlobalSettings = new GlobalSettings
                {
                    PaperSize = PaperKind.A4,
                    DPI = DPI,
                    Margins = Margins
                }
            });
        }

        private string ConnectionString(ILogger log, ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(context.FunctionAppDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            var connString = config.GetConnectionString("MyConnectionString");

            if(connString == null){log.LogInformation("Connection String is null");}

            return connString;
        }

        // The first arugment tells that the functions can be triggerd by a POST HTTP request. 
        // The second argument is mainly used for logging information, warnings or errors
        [FunctionName("Html2Pdf")]
        [STAThread]
        public async Task<string> Run([HttpTrigger(AuthorizationLevel.Function, "POST")] Html2PdfRequest Request, ILogger Log, ExecutionContext context)
        {
            Log.LogInformation("C# HTTP trigger function started for HTML2PDF.");
            Log.LogInformation(Request.HtmlContent);
            // PDFByteArray is a byte array of pdf generated from the HtmlContent 
            var PDFByteArray = BuildPdf(Request.HtmlContent, new MarginSettings(2, 2, 2, 2));

            // The connection string of the Storage Account to which our PDF file will be uploaded
            var StorageConnectionString = ConnectionString(Log, context);

            // Generate an instance of CloudStorageAccount by parsing the connection string
            var StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

            // Create an instance of CloudBlobClient to connect to our storage account
            CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

            // Get the instance of CloudBlobContainer which points to a container name "pdf"
            // Replace your own container name
            CloudBlobContainer BlobContainer = BlobClient.GetContainerReference("pdf");
            
            // Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded
            CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);
            
            Log.LogInformation("Attempting to upload " + Request.PDFFileName);
            // Upload the pdf blob
            await Blob.UploadFromByteArrayAsync(PDFByteArray, 0, PDFByteArray.Length);

            return "Created PDF Packing Slip.";
        }
    }
}

namespace pdfCreation
{
    public class Html2PdfRequest
    {
        // The HTML content that needs to be converted.
        public string HtmlContent { get; set; }
      
        // The name of the PDF file to be generated
        public string PDFFileName { get; set; }
    }
}

.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="DinkToPdf" Version="1.0.8" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.16" />
    <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.16" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
    <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0-preview1-25914-04" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
    <None Update="libwkhtmltox.dll">
     <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </None>
    <None Update="libwkhtmltox.dylib">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="libwkhtmltox.so">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="wwwroot\**\*">
     <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
     <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
  </ItemGroup>
</Project>
4

1 回答 1

0

好的,所以我似乎已经解决了这个问题。我仍然收到“Qt:无法初始化 OLE(错误 80010106)”错误消息,但我可以不断地点击 API 并继续生成 PDF,这样就可以解决一个问题。

如果有人遇到此问题并遇到问题,那么我的解决方案的代码如下:

.csproj 文件:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v3</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="DinkToPdf" Version="1.0.8" />
    <PackageReference Include="Microsoft.Azure.Functions.Extensions" Version="1.1.0" />
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.13" />
    <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.13" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
    <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.5.0-preview1-25914-04" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
    <None Update="libwkhtmltox.dll">
     <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </None>
    <None Update="libwkhtmltox.dylib">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="libwkhtmltox.so">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="wwwroot\**\*">
     <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
     <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
  </ItemGroup>
</Project>

函数应用文件:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using DinkToPdf;
using IPdfConverter = DinkToPdf.Contracts.IConverter;

[assembly: FunctionsStartup(typeof(pdfCreation.Startup))]

namespace pdfCreation
{
    public class Startup : FunctionsStartup
    {
        public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            FunctionsHostBuilderContext context = builder.GetContext();
            builder.ConfigurationBuilder.AddEnvironmentVariables();
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton(typeof(IPdfConverter), new SynchronizedConverter(new PdfTools()));
        }
    }

    public class Html2Pdf
    {
        // Read more about converter on: https://github.com/rdvojmoc/DinkToPdf
        // For our purposes we are going to use SynchronizedConverter
        //IPdfConverter pdfConverter = new SynchronizedConverter(new PdfTools());

        //Note we are using SynchronisedConverter / IPDFConverter via dependency injection
        private readonly IPdfConverter pdfConverter;
        public Html2Pdf(IPdfConverter pdfConverter)
        {
            this.pdfConverter = pdfConverter;
        }

        // A function to convert html content to pdf based on the configuration passed as arguments
        // Arguments:
        // HtmlContent: the html content to be converted
        // Margins: the margis around the content
        // DPI: The dpi is very important when you want to print the pdf.
        // Returns a byte array of the pdf which can be stored as a file
        private byte[] BuildPdf(string HtmlContent, MarginSettings Margins, int? DPI = 180)
        {
            // Call the Convert method of IPdfConverter / SynchronisedConverter "pdfConverter"
            return pdfConverter.Convert(new HtmlToPdfDocument()
            {
                // Set the html content
                Objects =
                {
                    new ObjectSettings
                    {
                        HtmlContent = HtmlContent,
                        WebSettings = { DefaultEncoding = "UTF-8", LoadImages = true }
                    }
                },
                // Set the configurations
                GlobalSettings = new GlobalSettings
                {
                    PaperSize = PaperKind.A4,
                    DPI = DPI,
                    Margins = Margins
                }
            });
        }

        private string ConnectionString(ILogger log, ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(context.FunctionAppDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

            var connString = config.GetConnectionString("ConnectionString");            

            if(connString == null){log.LogInformation("Connection String is null");}

            return connString;
        }

        // The first arugment tells that the functions can be triggerd by a POST HTTP request. 
        // The second argument is mainly used for logging information, warnings or errors
        [FunctionName("Html2Pdf")]
        [STAThread]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "POST")] Html2PdfRequest Request, ILogger Log, ExecutionContext Context)
        {
            try{
                Log.LogInformation("C# HTTP trigger function started for HTML2PDF.");
                Log.LogInformation(Request.HtmlContent);

                // PDFByteArray is a byte array of pdf generated from the HtmlContent 
                var PDFByteArray = BuildPdf(Request.HtmlContent, new MarginSettings(2, 2, 2, 2));

                // The connection string of the Storage Account to which our PDF file will be uploaded
                var StorageConnectionString = ConnectionString(Log, Context);

                // Generate an instance of CloudStorageAccount by parsing the connection string
                var StorageAccount = CloudStorageAccount.Parse(StorageConnectionString);

                // Create an instance of CloudBlobClient to connect to our storage account
                CloudBlobClient BlobClient = StorageAccount.CreateCloudBlobClient();

                // Get the instance of CloudBlobContainer which points to a container name "pdf"
                // Replace your own container name
                CloudBlobContainer BlobContainer = BlobClient.GetContainerReference("pdf");
                
                // Get the instance of the CloudBlockBlob to which the PDFByteArray will be uploaded
                CloudBlockBlob Blob = BlobContainer.GetBlockBlobReference(Request.PDFFileName);
                
                Log.LogInformation("Attempting to upload " + Request.PDFFileName);
                // Upload the pdf blob
                await Blob.UploadFromByteArrayAsync(PDFByteArray, 0, PDFByteArray.Length);

                return (ActionResult)new OkObjectResult(Request.PDFFileName + " was successfully created.");
            }
            catch(Exception e)
            {
                Log.LogInformation("Error occurred: " + e);
                return (ActionResult)new OkObjectResult("Error" + e.Message);
            }
            
        }
    }
}

namespace pdfCreation
{
    public class Html2PdfRequest
    {
        // The HTML content that needs to be converted.
        public string HtmlContent { get; set; }
      
        // The name of the PDF file to be generated
        public string PDFFileName { get; set; }
    }
}

我应该强调这里的变化:

  1. 这些版本需要更改为 3.1.13
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.13" />
    <PackageReference Include="Microsoft.Extensions.Http" Version="3.1.13" />

更新了这部分代码如下:

public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
            FunctionsHostBuilderContext context = builder.GetContext();
            builder.ConfigurationBuilder.AddEnvironmentVariables();
        }

        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton(typeof(IPdfConverter), new SynchronizedConverter(new PdfTools()));
        }
    }
private readonly IPdfConverter pdfConverter;
public Html2Pdf(IPdfConverter pdfConverter)
{
    this.pdfConverter = pdfConverter;
}

另外,不要忘记在函数应用程序的配置/应用程序设置下添加您的连接字符串。

希望这可以帮助某人!

于 2021-07-13T00:19:57.547 回答