はじめに
上司から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を使うときはfastapi
とuvicorn
は必須
ファイルをアップロードするときは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_bytes
でpaths_only=True
にしないとメモリをかなり食うので注意が必要です。
実行
Dockerの起動
-
Build
docker build -t fastapi .
-
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は非常に書きやすいと感じました。