3
2

More than 3 years have passed since last update.

FastAPIとDockerでPDFファイルをTIF画像に変換するAPIを作る

Posted at

はじめに

上司からFastAPIが良い感じらしいと聞いたので触ってみました。
シンプルにGETリクエストして文字を返すのではつまらないので、PDFファイルをTIF画像に変換するAPIを作成しました。

FastAPIとは

FastAPIはPythonのWeb frameworkで、Flaskに似たようなものです。

開発環境

  • Windows10 Pro
  • Docker for Windows

実装

ディレクトリ構成

root
├─app.py
├─Dockerfile
├─requirements.txt
└─test.pdf

Dockerfile

Dockerfile
FROM python:3.8

# PDFの変換に必要なpopplerをインストール
RUN apt-get update && \
    apt-get install -y poppler-utils

# Pythonモジュールのインストール
COPY requirements.txt .
RUN pip install --upgrade pip && \
    pip install -r requirements.txt && \
    rm requirements.txt

# 変換したファイルを一時保存するためのフォルダを作成
RUN rm -rf /app && \
    mkdir -p /app/data/

# プログラムを配置
COPY app.py /app/app.py

EXPOSE 8000
WORKDIR /app
CMD ["uvicorn", "app:api", "--host", "0.0.0.0", "--port", "8000"]

今回はpython:3.8のイメージを使用しましたが、Pythonが動いてpopplerがインストールできれば何でもいいです。

requirements.txt

requirements.txt
fastapi
uvicorn
python-multipart
pdf2image

FastAPIを使うときはfastapiuvicornは必須
ファイルをアップロードするときはpython-multipartが必要
PDFファイルを画像に変換するためにpdf2imageが必要

app.py

app.py
import os
from base64 import b64encode

import uvicorn
from fastapi import FastAPI, File, UploadFile
from pdf2image import convert_from_bytes
from PIL import Image

api = FastAPI()


@api.post("/")
async def post(file: UploadFile = File(...)):
    pdf_file = await file.read()
    tif_file = convert(pdf_file)
    return tif_file


def convert(pdf_file):
    output_folder = "./data"
    file_name = "temporary"
    output_file_path = f"{output_folder}/{file_name}.tif"

    # PDFの全ページをそれぞれjpgに変換して保存する
    image_path = convert_from_bytes(
        pdf_file=pdf_file,
        thread_count=5,
        fmt="jpg",
        output_folder=output_folder,
        output_file=file_name,
        paths_only=True,
    )

    # 全jpg画像を読み込む
    images = [Image.open(image) for image in image_path]

    # 全jpg画像を1枚のTIF画像に変換して保存する
    images[0].save(
        output_file_path, format="TIFF", save_all=True, append_images=images[1:],
    )

    # 全jpg画像を読み込み、base64でエンコードする
    with open(output_file_path, "rb") as f:
        tif_file = b64encode(f.read())

    # 保存している画像をすべて削除して、TIF画像のバイナリを返す
    for image in image_path:
        os.remove(image)
    os.remove(output_file_path)
    return tif_file


if __name__ == "__main__":
    uvicorn.run(api)

convert_from_bytespaths_only=Trueにしないとメモリをかなり食うので注意が必要です。

実行

Dockerの起動

  1. Build

    docker build -t fastapi .
    
  2. Run

    docker run --rm -it -p 8000:8000 fastapi
    

APIリクエスト

 > curl -X POST -F 'file=@./test.pdf' http://localhost:8000 | base64 -di > ./test.tif
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  100  206M  100  206M  100  309k  27.0M  41409  0:00:07  0:00:07 --:--:-- 47.2M

base64でエンコードされて返却されるので、base64でデコードして書き込む必要があります。

まとめ

ファイルをアップロードするのにpython-multipartが必要だったり、少し躓くところはありましたが、FastAPIは非常に書きやすいと感じました。

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