Azure 함수에서 Razor 템플릿 및 Puppetersharp을 사용하여 PDF 렌더링

몇 주 전에 우리는 어떻게 캡처를 사용하여 웹 페이지를 얻는지 연구했다.오늘은 Azure 함수에서 Razor와 C#port of Puppeter PuppeteerSharp을 사용하여 PDF를 표시하는 방법을 알아보겠습니다.
이런 방법의 흔히 볼 수 있는 용법은 유사한 영수증을 만드는 내용이다.우리는 우리가 가장 좋아하는 가상 온라인 상점 Tailwind Traders을 위해 PDF 영수증을 만들 것이다.

개요


ASP를 실행합니다.NET Core Razor 페이지.NET Azure 기능 응용 프로그램과 무두크롬이 있는 Puppetersharp를 사용하여 영수증 면도기 템플릿을 PDF로 보여줍니다.

본문의 나머지 부분에서 우리는 다음과 같은 절차를 소개할 것이다.
  • 구축 ASP.NET Core Razor Pages 응용 프로그램은 Razor 템플릿과 송장 제출에 필요한 추가 리소스를 포함합니다.
  • 은 Azure Functions 응용 프로그램을 만들고 PuppeterSharp를 실행하도록 구성합니다.
  • 은 ASP를 실행합니다.기능 응용 프로그램의 NET Core Razor Pages 응용 프로그램입니다.
  • 은 PuppeterSharp로 영수증을 보여주고 PDF를 생성하는 함수를 작성합니다.
  • 은 Azure에 적용됩니다.
  • ASP를 생성합니다.NET Core Razor Pages 애플리케이션


    우리는 전형적인 ASP를 사용할 것이다.NET Core Razor Pages 응용 프로그램에서 청구서를 보여 줍니다.이것은 단지 dotnet new webapp으로 만든 간단한 프로젝트일 뿐이다.
    응용 프로그램에서 우리는 하나의 텍스트 상자를 가진 폼을 만들 것이다.Puppeter는 일부 JSON 데이터로 양식을 채워 Razor 페이지로 전달합니다.양식을 제출하면 Razor가 청구서를 제출합니다.
    이것은 Razor 코드입니다.
    @page
    @model IndexModel
    
    <div class="text-center">
      @* <h1 class="display-4"><img class="logo" src="img/functions.svg" /> Invoice</h1> *@
    
      <div class="logo">
        <img class="logo" src="img/ttlogo.png" />
      </div>
      @if (Model.Items == null)
      {
        <div>
          <form id="invoice-form" method="post">
            <textarea name="items" id="items-box"></textarea>
            <input type="submit" id="submit-button" value="Submit" />
          </form>
        </div>
      }
      else
      {
        <div>
          <table class="table">
            <thead>
              <tr>
                <td>Item #</td>
                <td>Name</td>
                <td>Price</td>
                <td>Quantity</td>
                <td>Line Total</td>
              </tr>
            </thead>
            <tbody>
              @foreach(var item in Model.Items)
              {
                <tr>
                  <td>@item.ProductId</td>
                  <td>@item.Name</td>
                  <td>@item.UnitPrice.ToString("F2")</td>
                  <td>@item.Quantity</td>
                  <td>@((item.UnitPrice * item.Quantity).ToString("F2"))</td>
                </tr>
              }
              <tr>
                <td colspan="4"></td>
                <td><b>@(Model.Items
                        .Select(i => i.UnitPrice * i.Quantity)
                        .Sum()
                        .ToString("F2"))</b></td>
              </tr>
            </tbody>
          </table>
        </div>
      }
    </div>
    
    이것은 OnPost 방법으로 JSON 송장 데이터를 가져와 페이지 모델로 변환합니다.
    public void OnPost()
    {
        var options = new JsonSerializerOptions
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        };
        var itemsJson = Request.Form["items"].First();
    
        Items = JsonSerializer.Deserialize<IEnumerable<InvoiceItem>>(itemsJson, options);
    }
    

    For more information on how to get started with Razor Pages, check out this tutorial.


    이를 테스트하려면 dotnet run을 실행하고 상자에 제품을 제출하십시오.


    C#Azure 함수에서 puppetersharp 사용하기


    다른 프로젝트에서는 Azure 기능 응용 프로그램을 만들 것입니다.응용 프로그램에서 PuppeteerSharp에 대한 인용을 추가합니다.
    노드.js Puppeter 패키지는 npm 설치 기간에 Chromium을 다운로드하지만 PuppeterSharp는 없습니다. 반대로 응용 프로그램에서 BrowserFetcher 클래스를 사용하여 브라우저를 다운로드해야 합니다.
    우리는 시작할 때 브라우저를 다운로드할 수 있는 기능 프로그램을 설정할 수 있다.
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var bfOptions = new BrowserFetcherOptions();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            bfOptions.Path = Path.GetTempPath();
        }
        var bf = new BrowserFetcher(bfOptions);
        bf.DownloadAsync(BrowserFetcher.DefaultRevision).Wait();
        var info = new AppInfo
        {
            BrowserExecutablePath = bf.GetExecutablePath(BrowserFetcher.DefaultRevision)
        };
        builder.Services.AddSingleton(info);
    }
    
    Linux에서 우리는 기계의 임시 폴더를 다운로드하고 있습니다.Azure의 Linux 소비 계획에 배치할 때 기본 다운로드 위치 (응용 프로그램의 bin 폴더) 는 읽기 전용이기 때문에 이렇게 해야 합니다.
    브라우저를 다운로드한 후, 실행 가능한 파일의 경로를 AppInfo 대상에 숨깁니다. 이 대상은 우리의 함수에 주입할 수 있습니다.다운로드한 위치에서 브라우저를 불러올 수 있도록 인용할 것입니다.

    Note: Downloading the app at function app startup will increase cold start time significantly. If you find that cold start is too slow, explore other ways to download the browser, such as downloading it to shared mounted storage. This storage can be shared between different function app instances, so subsequent instances do not have to download the browser again.


    Azure 기능에서 Razor 응용 프로그램 참조 및 시작


    이제 Razor Pages 응용 프로그램과 function 응용 프로그램이 생겼습니다. 이 두 가지를 통합해야 합니다.
    function 응용 프로그램에서 Razor Pages 응용 프로그램에 대한 참조를 추가했습니다.
    dotnet add reference ../RazorPagesApp/RazorPagesApp.csproj
    
    이것은 바이너리 파일을 Razor Pages 프로그램에서 function 프로그램으로 구축하고 복사합니다.이미지와 같은 정적 자원이 있기 때문에 복제하기 위해 정적 자원을 설정해야 합니다.이를 위해 함수 프로그램의 프로젝트 파일을 업데이트하여 Razor Pages 프로그램의 wwwroot 폴더에 복사합니다.
    <ItemGroup>
      <Content Include="..\RazorPagesApp\wwwroot\**" Link="wwwroot\%(RecursiveDir)%(Filename)%(Extension)" CopyToOutputDirectory="Always" />
    </ItemGroup>
    
    Puppeter의 헤더 없는 Chromium 브라우저가 영수증을 보여주기 위해서는 Razor Pages 응용 프로그램의 ASP가 필요합니다.NET 코어 서버.함수 프로그램이 시작될 때, 우리는 사용하지 않은 부분을 찾아 서버를 시작합니다.우리의 Configure 방법은 현재 다음과 같다.
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var bfOptions = new BrowserFetcherOptions();
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            bfOptions.Path = Path.GetTempPath();
        }
        var bf = new BrowserFetcher(bfOptions);
        bf.DownloadAsync(BrowserFetcher.DefaultRevision).Wait();
        var info = new AppInfo
        {
            BrowserExecutablePath = bf.GetExecutablePath(BrowserFetcher.DefaultRevision)
        };
    
        var port = GetAvailablePort();
        info.RazorPagesServerPort = port;
        builder.Services.AddSingleton(info);
    
        var webHost = Host.CreateDefaultBuilder()
            .ConfigureWebHostDefaults(webBuilder =>
            {
                var scriptRoot = Environment.GetEnvironmentVariable("AzureWebJobsScriptRoot");
                System.Console.WriteLine($"Starting web server on port {port}");
                if (!string.IsNullOrEmpty(scriptRoot))
                {
                    webBuilder.UseContentRoot(scriptRoot);
                }
    
                webBuilder.UseUrls($"http://0.0.0.0:{port}")
                    .UseStartup<RazorPagesApp.Startup>();
            })
            .Build();
    
        webHost.Start();
    }
    
    또한 선택한 포트를 AppInfo 개체에 숨깁니다. 이 개체는 나중에 주입에 의존하는 함수로 접근할 수 있습니다.
    이제 function 프로그램을 시작하면 브라우저가 다운로드되었는지 확인하고 랜덤 포트에서 Razor Pages 프로그램을 시작합니다.

    Razor 페이지를 PDF로 표시


    마지막으로 응용 프로그램에 추가해야 할 것은 인형으로 PDF를 렌더링하는 기능입니다.
    public class GeneratePdf
    {
        private readonly AppInfo appInfo;
    
        public GeneratePdf(AppInfo browserInfo)
        {
            this.appInfo = browserInfo;
        }
    
        [FunctionName("GeneratePdf")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req,
            ILogger log)
        {
            var browser = await Puppeteer.LaunchAsync(new LaunchOptions
            {
                Headless = true,
                ExecutablePath = appInfo.BrowserExecutablePath
            });
            var page = await browser.NewPageAsync();
            await page.GoToAsync($"http://localhost:{appInfo.RazorPagesServerPort}/");
    
            var data = GetData();
            await page.TypeAsync("#items-box", data);
            await Task.WhenAll(
                page.WaitForNavigationAsync(), 
                page.ClickAsync("#submit-button"));
            var stream = await page.PdfStreamAsync();
            await browser.CloseAsync();
    
            return new FileStreamResult(stream, "application/pdf");
        }
    }
    
    이 기능은 Puppeter를 사용하여 헤더 없는 Chrome 브라우저를 시작하여 Razor Pages 응용 프로그램에서 입력 폼을 열고 영수증 데이터를 제출하여 영수증을 보여주고 웹 페이지에서 PDF를 생성합니다.

    Azure에 배포


    응용 프로그램은 Chromium을 다운로드하고 정확한 위치로 다운로드하도록 설정되어 있기 때문에 응용 프로그램을 배치하는 데 특별한 작업이 필요하지 않습니다.Chrome은 Linux에서만 작동한다는 것을 기억하십시오.우리는 그것을 Linux 소비 계획에 배치할 수 있다.

    리소스

  • Sample code
  • PuppeteerSharp
  • 좋은 웹페이지 즐겨찾기