LoginSignup
1
1

More than 3 years have passed since last update.

AzureFunctions + puppeteer でPDF出力

Posted at

概要

AzureFunctions + puppeteerでPDFを出力する。
生成したPDFはAzureStorageに保存する。

How to

「AzureFunctions + puppeteer」でスクショを撮る方法は以下を参照。
https://anthonychu.ca/post/azure-functions-headless-chromium-puppeteer-playwright/

コード

実際に動かしたコードをちょっと変えています(大人の事情)。
動作確認してないので、動かないかもです。参考程度にご覧ください。
ガーと書いたので、ツッコミポイントは多いですが、スルーしてください。

import * as puppeteer from "puppeteer";
import { BlobServiceClient, BlockBlobClient } from "@azure/storage-blob";
import { AzureFunction, Context, HttpRequest } from "@azure/functions";
import * as util from "util";
import { jsPDF } from "jspdf";
import { promises as fs } from "fs";
import { exec } from "child_process";
import imageSize from "image-size";

const execAsync = util.promisify(exec);
const AZURE_STORAGE_CONNECTION_STRING ="接続文字";

/**
 * Azure Function上にフォントファイルを設置する
 * https://github.com/Azure/azure-functions-host/issues/4883#issuecomment-683380515
 */
const fontUpdate = async () => {
  const fontPath = process.env.FONT_PATH || "/home/.fonts";
  try {
    await fs.stat(fontPath);
    return;
  } catch {}

  // ln -s /home/site/wwwroot/fonts $FONT_PATH
  await execAsync(`ln -s /home/site/wwwroot/fonts ${fontPath}`);
};

const getBlockBlobClient = (name: string) => {
  const blobServiceClient = BlobServiceClient.fromConnectionString(
    AZURE_STORAGE_CONNECTION_STRING
  );
  const containerClient = blobServiceClient.getContainerClient("pdf");
  return containerClient.getBlockBlobClient(`${name}.pdf`);
};

const gotoPdfPage = async (
  page: puppeteer.Page
) => {
  // スクショ撮るページ
  const url = `https://www.google.com/?hl=ja`;
  await Promise.all([
    page.waitForNavigation({ waitUntil: ["load", "networkidle2"] }),
    page.goto(url),
  ]);
};

const screenShotPdf = async (page: puppeteer.Page) => {
  const image = (await page.screenshot({
    type: "jpeg",
    quality: 85,
    fullPage: true,
  })) as Buffer;
  return image;
};

const createPdf = (image: Buffer, name: string) => {
  const size = imageSize(image);

  const pdf = new jsPDF({
    format: "a4",
    unit: "mm",
    orientation: "p",
  });
  const ratio = pdf.internal.pageSize.height;

  pdf.addImage(
    image,
    "JPEG",
    0,
    0,
    size.width * ratio,
    size.height * ratio,
    "",
    "FAST"
  );
  const pdfPath = `/tmp/${name}.pdf`;
  pdf.save(pdfPath);

  return pdfPath;
};

const httpTrigger: AzureFunction = async function (
  context: Context,
  req: HttpRequest
): Promise<void> {
  const name = req.query?.name;

  if (name) {
    try {
      const pdfBlob = getBlockBlobClient(name);

      // 既に存在する場合は何もしない
      if (await pdfBlob.exists()) {
        context.res = {
          status: 200,
          body: "already exists",
        };
        return;
      }

      await fontUpdate();

      const browser = await puppeteer.launch({
        args: [
          "--no-sandbox",
          "--disable-setuid-sandbox",
          "--disable-web-security",
        ],
      });

      const page = await browser.newPage();
      await gotoPdfPage(page, name);

      const image = await screenShotPdf(page);

      const pdfPath = createPdf(image,name);

      pdfBlob.uploadFile(pdfPath);

      browser.close();
    } catch (e) {
      context.res = {
        status: 400,
        body: `error: ${e}`,
      };
      context.done();
      return;
    }

    context.res = {
      status: 200,
      body: "success",
    };
    context.done();
    return;
  } else {
    context.res = {
      status: 400,
      body: `name:${name}`,
    };
    context.done();
    return;
  }
};

export = httpTrigger;

大雑把な流れ

  1. AzureStorageに既に存在するかチェックする
  2. フォントを設定する
  3. ヘッドレスブラウザを起動
  4. ページに移動
  5. ページのスクリーンショットを取得
  6. スクショの画像をPDFのA4サイズにリサイズしてPDFに貼り付ける
  7. AzureStorageにPDFをアップロード

詰まりポイント

日本語のフォントを読み込む必要あり

postinstallpostinstall.shを動かします。

スクショの画像サイズ取得

画像のサイズを取得してA4サイズとの比率を計算する必要がありました。
この画像サイズを取得するのに詰まりました。
便利なライブラリimage-sizeを使用しました。
使いやすいです。

browserのオプション

以下のオプションを設定しないと、出力できませんでした。

      const browser = await puppeteer.launch({
        args: [
          "--no-sandbox",
          "--disable-setuid-sandbox",
          "--disable-web-security",
        ],
      });

一時ファイルの保存場所

一時的にファイルを保存したい場合、/tmp/*に保存する。
AzureFunctionsの仕様?

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1