PDFデータを画像データへ変換するAzure Function(Python)を用いたアーキテクチャを構築する(サンプルコードあり)

2024/03/15に公開

はじめに

PDFデータを画像データへ変換するAzure Functionsを実装したのでこの記事でまとめたいと思います。
PDFデータの画像データへの変換は処理に時間がかかるケースも懸念されるため、FaaS系のサービスサービスを使用したアーキテクチャを採用するのは実装観点・運用観点ともにメリットが多いです。
今回は、AzureのFaaSサービスであるAzure Functionsを使用してアーキテクチャを構築し、実装まで実施したのでサンプルコード含めて紹介したいと思います。

Azure Functionsとは

公式:Azure Functionsの概要

Azure Functions は、記述するコードと管理するインフラストラクチャを減らし、コストを節約できるサーバーレス ソリューションです。

Azure Functionsは、Azureが提供するFaaS(Function as a Service)サービスです。
AWSだとAWS Lambda、GCPだとCloud Functionが提供されています。

Azureサービスを使ったアーキテクチャ設計

それぞれ処理フローを起点に、アーキテクチャ設計について解説していきたいと思います。
① ユーザー(クライアント)からAPIサーバーへPDFデータを送信する
② APIサーバーからBlob StorageへPDFデータを書き込む
③ Event GridにてBlob StorageへのPDFデータ書き込みイベントをハンドリングする
④ Event GridからAzure Functionsへトリガーをかける
⑤ PDFデータを画像データへ変換しBlob Storageへ書き込む

① ユーザー(クライアント)からAPIサーバーへPDFデータを送信する

ここはシンプルなAPIへのリクエストとなります。APIリクエストのリクエストボディにPDFデータをセットし、APIサーバーへ送信します。

② APIサーバーからBlob StorageへPDFデータを書き込む

APIサーバーから、AzureのオブジェクトストレージサービスであるBlo StorageへPDFデータを書き込みます。

③ Event GridにてBlob StorageへのPDFデータ書き込みイベントをハンドリングする

Event GridにてBlob Storageへのデータ書き込みを検知し、Azure Functionsへのトリガーをかけます。

④ Event GridからAzure Functionsへトリガーをかける

Azure Functionsでは、Event Gridトリガーを使用するとEvent Gridからのイベント配信をサブスクライブすることができます。
今回のケースでは、Blob StorageへのPDFデータ書き込みイベントをトリガーに、Event Gridを経由して、Azure Functionsを発火させるフローとなります。

⑤ PDFデータを画像データへ変換しBlob Storageへ書き込む

Azure FunctionsにてPDFデータを画像データへ変換し、その画像データをBlob Storageへ書き込みます。
Function内での変換処理は「AzureFunctionのサンプルコード」にて後述します。

Azure Functionsを使った、PDFデータの画像データへの変換

Azure Functionsを使ったPDFデータの画像データへの変換についてコードベースで解説していきます。

function_app.py
import logging
import io
import os
import re
from urllib.parse import urlparse
from azure.cosmos import CosmosClient
import azure.functions as func
from azure.storage.blob.aio import BlobServiceClient
from azure.storage.blob import __version__, ContentSettings
from pdf2image import convert_from_bytes


# Declare constraints variables.
AZ_COSMOS_CONNECTION_STRING = os.getenv("AZ_COSMOS_CONNECTION_STRING")
AZ_COSMOS_DB_ID = "<AZ_COSMOS_DB_ID>"
AZ_COSMOS_CONTAINER = "<AZ_COSMOS_CONTAINER>"
AZ_ST_CONNECTION_STRING = os.getenv("AZ_ST_CONNECTION_STRING")

# Setting up
blob_service_client = BlobServiceClient.from_connection_string(
    AZ_ST_CONNECTION_STRING)
my_cosmos_client = CosmosClient.from_connection_string(AZ_COSMOS_CONNECTION_STRING)
database = my_cosmos_client.get_database_client(AZ_COSMOS_DB_ID)
my_container = database.get_container_client(AZ_COSMOS_CONTAINER)

app = func.FunctionApp()


@app.function_name(name="Convert")
@app.event_grid_trigger(arg_name="event")
async def ConvertPDF2Image(event: func.EventGridEvent):
    try:
        # 1. Getting container name and blob name from event data.
        data = event.get_json()
        o = urlparse(data['url'])
        m = re.match('/(?P<container_name>[\w-]+)/(?P<blob_name>.+)', o.path) # Blob Storageの構造によって処理が変わります
        container_name = m.groupdict()["container_name"]
        blob_name = m.groupdict()["blob_name"]

        # 2. Downloading PDF data from Blob Storage.
        download_blob_client = blob_service_client.get_blob_client(
            container=container_name, blob=blob_name)
        stream = await download_blob_client.download_blob()
        pdf_bytes_io = io.BytesIO(await stream.readall())
        pdf_bytes_data = pdf_bytes_io.getvalue()
        logging.info("#2. Downloading PDF data from Blob Storage is done.")

        # 3. Converting PDF to image.
        images = convert_from_bytes(
            pdf_bytes_data,
            fmt="jpeg",
            dpi=200
        )
        image_pillow = images[0]
        image_bytes_io = io.BytesIO()
        image_pillow.save(image_bytes_io, format="JPEG")
        image_bytes_data = image_bytes_io.getvalue()
        logging.info("#3. Converting PDF to image is done.")

        # 4. Uploading image to Blob Storage.
        upload_blob_client = blob_service_client.get_blob_client(
            container=container_name,
            blob="my-image.jpg"
        )
        await upload_blob_client.upload_blob(
            image_bytes_data,
            blob_type="BlockBlob",
            content_settings=ContentSettings(content_type="image/jpg"),
            overwrite=True
        )
        logging.info("#4. Uploading image to Blob Storage is done.")

    except Exception as e:
        raise Exception(e)

    finally:
        logging.info("Converting PDF to image is done.")

関数内での処理フローは下記の通りです。

  1. Event GridからサブスクライブしたEventデータから、PDFデータが格納されているBlob Storageのコンテナ名とBlob名を取得する
  2. Blob StorageからPDFデータをダウンロードする
  3. PDFデータを画像データへ変換する
  4. Blob Storageへ画像データをアップロードする

今回は、Event Grid関連の設定については説明を割愛します。設定方法については下記ドキュメントを参考にしてください。
(公式Doc)チュートリアル: イベント サブスクリプションを使用して BLOB コンテナーで Azure Functions をトリガーする

1. Event GridからサブスクライブしたEventデータから、PDFデータが格納されているBlob Storageのコンテナ名とBlob名を取得する

Event Gridから取得したEventデータより、PDFデータが格納されているBlob Storageのコンテナ名とBlob名を取得します、
※ Blob Storageの(仮想)ディレクトリ構造によって実装が異なる点に注意してください。

2.Blob StorageからPDFデータをダウンロードする

Python用Azure Blob Storage SDKを使って、PDFデータをBlob Storageからダウンロードします。

3.PDFデータを画像データへ変換する

PDFデータを画像データへ変換します。
PDFデータの画像データへの変換は、pdf2imageを使用しています。細かな変換に関する設定は、必要な要件に合わせてセットしてください。今回は、JPEG形式を指定して変換します。

※ 変換ライブラリはいくつか選択肢あるので、用途やライセンスとの兼ね合いを考慮して最適なライブラリを選定して使用してください。

4.Blob Storageへ画像データをアップロードする

変換した画像データをBlob Storageへアップロードします。
アップロードに関しても、「2.Blob StorageからPDFデータをダウンロードする」で紹介したAzure Blob Storage SDKを使用して実現可能です。
※ Blob Storageの(仮想)ディレクトリ構造によって実装が異なる点に注意してください。

まとめ

今回は、Azure Functionsを使ったPDFデータを画像データへ変換する機能のアーキテクチャ構築およびPythonを使った実装を紹介しました。
Azure FunctionsはBlob Storageへの書き込みイベントをトリガーに発火できるなど、サーバーレスアーキテクチャの構築をするための機能がたくさん用意されております。
PDFデータの変換は、処理負荷が重くなる可能性や、機能としてビジネスロジックから切り離せるなどの要件からFaaSサービスを用いて実装するのは非常に相性がいいと感じました。

株式会社log build

Discussion