3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Cloud FunctionsでPDFを作成 => Firebase Storageにアップロード => 署名付きURLを返す方法

Last updated at Posted at 2020-10-06

仕事でCloud functionsを用いてPDF作成機能を実装することになったので、その時のメモです。

やりたかったこと

  1. Cloud functionsにてonCallでデータを受け取る
  2. 受け取ったデータを元にPDFを作成する
  3. 作成したPDFをfirebase storageに保存する
  4. 保存したファイルのダウンロードリンクを返す

では、いきましょう。

全体のコード

module.exports = functions
  .runWith(Constant.runtimeOptions)
  .region("asia-northeast1")
  .https.onCall(async (data, context) => {
    if (!context.auth) {
      console.error("認証されていないユーザーからのアクセスです。");
      throw new functions.https.HttpsError(
        "failed-precondition",
        "認証されていないユーザーからのアクセスです。"
      );
    }

    const noData = " -";
    let sample1 = data.sample1;
    let sample2 = data.sample2;
    
    try {
      // A4サイズのPDFでマージンを50にする
      const doc = new pdf({ size: "A4", margin: 50 });

      // データをメモリで受け取るStreamを初期化
      const memoryStream = new MemoryStream(null, {
        readable: false,
      });

      // docにmemoryStreamを設定
      doc.pipe(memoryStream);

      // PDFで用いるフォントを読み込む
      doc.font("fonts/ipaexm.ttf");

      // y軸
      let currentYAxis = 0;

      // タイトル
      currentYAxis += 80;
      doc.fontSize(18).text("詳 細", 0, currentYAxis, {
        width: 600,
        align: "center",
      });

      // サブタイトル
      currentYAxis += 70;
      doc.fontSize(14).text("サブタイトル", 50, currentYAxis);

      // サンプル1
      if (typeof sample1 !== "string") {
        sample1 = noData;
      }
      currentYAxis += 30;
      buildTitleValueTextWithLine({
        doc: doc,
        title: "サンプル1",
        value: sample1,
        yAxis: currentYAxis,
        LineFromXAxis: 140,
        lineToXAxis: 250,
      });

      // サンプル2
      if (typeof sample2 !== "string") {
        sample2 = noData;
      }
      currentYAxis += 30;
      buildTitleValueTextWithLine({
        doc: doc,
        title: "サンプル2",
        value: sample2,
        yAxis: currentYAxis,
        LineFromXAxis: 140,
        lineToXAxis: 250,
      });

      // 書き込み完了
      doc.end();

      let isCreatePdfEnded = false;
      let url: any = "";
      doc.on("end", async () => {
        const fileName = `sample.pdf`;
        
        // メモリーに書き込んだデータをバッファーにいれる
        const buffer = Buffer.concat(memoryStream.queue);

        // firebase storageへの参照を作成
        const file = admin
          .storage()
          .bucket("bucket_name")
          .file(`directory/${fileName}`);

        // fileの保存
        await file.save(buffer, {
          metadata: { contentType: "image/pdf" },
        });

        // 認証用トークン付きのダウンロードURLを取得
        url = await file.getSignedUrl({
          action: "read",
          expires: moment().add(2, "minutes").toDate(),
        });

        // PDF作成完了フラグをtrueに
        isCreatePdfEnded = true;
      });

      // PDF作成完了フラグがtrueになるまで1秒ずつ待ち続ける
      while (!isCreatePdfEnded) {
        await sleepByPromise(1);
      }

      // urlは配列で返ってくるため、stringとして返す
      return {
        url: url[0],
      };
    } catch (e) {
      console.error(e);
      throw new functions.https.HttpsError(
        "internal",
        "PDF出力中にエラーが発生しました。"
      );
    }
});

function buildTitleValueTextWithLine({
  doc,
  title,
  value,
  yAxis,
  LineFromXAxis,
  lineToXAxis,
}: {
  doc: any;
  title: string;
  value: string;
  yAxis: number;
  LineFromXAxis: number;
  lineToXAxis: number;
}) {
  // x軸が50で、y軸がyAxisの場所からフォント10pxの文字をかく
  doc.fontSize(10).text(title, 50, yAxis);

  // x軸が140で、y軸がyAxisの場所からフォント10pxの文字をかく
  doc.fontSize(10).text(value, 140, yAxis);

  // x軸がLineFromXAxisからlineToXAxis、y軸がyAxis + 15の範囲に下線を引く
  doc
    .moveTo(LineFromXAxis, yAxis + 15)
    .lineTo(lineToXAxis, yAxis + 15)
    .stroke();
}

export const sleepByPromise = function (sec) {
    return new Promise(resolve => setTimeout(resolve, sec * 1000));
}

用いているライブラリ

3
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?