LoginSignup
2
5

More than 3 years have passed since last update.

HTMLをPOSTするとPDFに変換して返すDockerコンテナの作り方

Last updated at Posted at 2020-04-28

先日公開したウェブサービスの制作過程で得た知見を、シリーズで発信していく記事の第4弾です。
個人開発でウェブサービスにトライしてみたいと考えている方の参考になりましたら嬉しいです。

公開したウェブサービスは、ウェブサイト制作業務を楽に進めるためのツールなのですが、そのツールには企画をまとめるためのメモ機能を搭載しています。

そのメモ機能は、マークダウン記法で記述できるようになっており、PlantUMLも使用できるよう拡張しています。このメモをPDFとして出力出来たら、そのまま企画書として提出できて便利だろうなと思いまして、HTMLをPDFに変換するDockerコンテナを作りました。

wkhtmltopdfを利用してHTMLをPDFに変換する

HTMLをPDFに変換する方法については、OSSのwkhtmltopdfを利用することにしました。

HTMLソースをPOSTするだけでPDFが返却されるようにしたかったので、サーバとしての機能が必要です。今回はnode.jsとexpressを使ってお手軽にサーバを立てることにします。

Dockerfileはnode.jsイメージをベースとし、それにwkhtmltopdfと日本語フォントをインストールしたDockerイメージを作ることを目標にします。

Dockerfile

FROM node:12.16.2-stretch

ENV LANG C.UTF-8
ARG DEBIAN_FRONTEND=noninteractive

ENV HOST 0.0.0.0
EXPOSE 3000

RUN apt-get update -qq \
 && apt-get install -y \
      build-essential \
      xorg \
      libssl-dev \
      libxrender-dev \
      wget \
      unzip \
      gdebi \
 && apt-get autoremove \
 && apt-get clean

# wkhtmltopdf をインストールする。
RUN wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb \
     && apt-get install ./wkhtmltox_0.12.5-1.stretch_amd64.deb

# 日本語フォントとして Noto Fonts をインストールする。
RUN wget https://noto-website.storage.googleapis.com/pkgs/Noto-unhinted.zip \
 && unzip -d NotoSansJapanese Noto-unhinted.zip \
 && mkdir -p /usr/share/fonts/opentype \
 && mv -fv ./NotoSansJapanese /usr/share/fonts/opentype/NotoSansJapanese \
 && rm -rfv Noto-unhinted.zip \
 && fc-cache -fv

WORKDIR /usr/src

COPY ./package*.json ./

RUN npm install --no-optional

COPY . .

CMD ["node", "index.js"]

ポイントはstretchベースのnodeイメージを使用する点です。
そこに、stretchに対応したwkhtmltopdfと必要なライブラリをインストールしていきます。

ただし、それだけではPDFファイルの日本語が文字化けしますので、Notoフォントをインストールしてフォントキャッシュに反映させておきます。

index.js

まずはnpmでexpressとwkhtmltopdfパッケージをインストールします。

npm i express wkhtmltopdf

当初、wkhtmltopdfの呼び出しは、child_processを通してコマンドラインで行おうとしたのですが、うまくいきませんでした。コンテナに入ってコマンドラインから呼び出すとPDFに変換できるのですが、node.jsのchild_processから呼び出すと、wkhtmltopdfがうまく機能しないようです。(Githubにも同様のやりとりが見つかりました。)

node.jsをやめてNginx + PHPのコンテナに変更しようかと迷っていたところ、「wkhtmltopdf」というnpmパッケージを発見し、そちらを試したところ無事に変換することができました。

以下のようなコードでOKです。
※最小限のエラー処理しか書いていませんので、用途に合わせて適宜追加してください。

index.js
const express = require('express')
const wkhtmltopdf = require('wkhtmltopdf')

const app = express()
app.use(express.json())
app.listen(3000)

app.post('/', (req, res, next) => {
  try {
    const html = req.body.html
    wkhtmltopdf(html).pipe(res)
  } catch (err) {
    next(err)
  }
})

app.use((err, _req, res, next) => {
  if (res.headersSent) {
    return next(err)
  }
  res.status(500).send('エラーが発生しました')
})

もしダウンロードするファイル名を指定したいときは、resのヘッダーにファイル名を追加すればOKです。
試してないですが、多分以下の感じでいけるのでは。(ファイル名に日本語を使うと、ブラウザによって挙動が異なることがあるので、できれば避けたいところ)

const fileName = 'hogehoge.pdf'
res.set('Content-Disposition', `attachment; filename=${fileName}`)

各ファイルを以下のように配置して、Dockerfileをビルドします。

html2pdf/
 ├ Dockerfile
 ├ index.js
 ├ package.json
 └ package-lock.json

コンテナを立ち上げれば、目的のサーバの完成です。(Docker最高!)

今回の例では、3000番ポートで待ち受けますので、HTMLソースを3000番ポートに向けてPOSTすれば、PDFファイルが返却されます。

const html = '< htmlソース >'
axios.post('http://localhost:3000', { html })

これまでは、PDF出力が必要な場合、PHPとTCPDFを使うことが多かったのですが、こちらの方が格段に楽で良いですね。HTMLとcssならば、凝ったレイアウトも比較的かんたんに実現できますし、このコンテナを起動しておけば、いろいろなところから呼び出して使いまわせて便利です。

最後に、宣伝です。。。

この記事の冒頭で触れたウェブサービスでも、これに近い方法でコンテナを走らせています。

WEBサイト制作者さん向けのサービスでして、お気軽にお試しいただけると幸いです。(無料で使えます)

2
5
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
2
5