PDFアップロード・表示アプリを作成する

Flask, DynamoDB, Lambda, S3, Zappaを使ってPDFアップロード・表示アプリを作成する #

はじめに

このチュートリアルでは、Flask, DynamoDB, Lambda, S3, Zappaを使用して、PDFをアップロードし、S3に保存し、表示するアプリケーションを作成します。

1. 環境の準備 #

まず、新しいディレクトリを作成し、仮想環境を設定します。

$ mkdir pdf-upload-app
$ cd pdf-upload-app
$ python3 -m venv venv
$ source venv/bin/activate

次に、必要なパッケージをインストールします。

$ pip install flask boto3 zappa requests

requestsライブラリを使用して、プリサインされたURLからPDFデータを取得し、そのデータをレスポンスとして返します。

プロジェクトの構成 #

プロジェクトディレクトリを作成し、次のような構成でファイルを作成します。

pdf-upload-app/
  ├── app.py
  ├── templates/
  │   ├── index.html
  │   └── upload.html
  └── zappa_settings.json

S3バケットの設定 #

AWSコンソールでS3バケットを作成し、バケット名をメモしておきます。

app.pyとzappa用に二つ必要です。

DynamoDBテーブルの設定 #

AWSコンソールでDynamoDBテーブルを作成し、テーブル名とパーティションキーをメモしておきます。

PDFのアップロードと表示機能の実装 #

app.pyにPDFのアップロードと表示機能を実装します。

[app.py]

from flask import Flask, render_template, request, redirect, url_for, Response
import boto3
import os
from werkzeug.utils import secure_filename 
from uuid import uuid4
from botocore.exceptions import NoCredentialsError
import requests

app = Flask(__name__)

# S3およびDynamoDBの設定
S3_BUCKET = 'your-s3-bucket-name'
DYNAMO_TABLE = 'your-dynamodb-table-name'
s3 = boto3.client('s3')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(DYNAMO_TABLE)

ALLOWED_EXTENSIONS = {'pdf'}

def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    response = table.scan()
    pdfs = response['Items']
    return render_template('index.html', pdfs=pdfs)

@app.route('/upload', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        if 'file' not in request.files:
            return redirect(request.url)

        file = request.files['file']

        if file.filename == '':
            return redirect(request.url)

        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file_extension = filename.split('.')[-1]
            unique_filename = f"{uuid4()}.{file_extension}"
            pdf_id = str(uuid4())
            try:
                s3.upload_fileobj(file, S3_BUCKET, unique_filename)
                table.put_item(
                    Item={
                        'id': pdf_id,
                        'file_name': unique_filename,
                    }
                )
                return redirect(url_for('view', pdf_id=pdf_id))
            except NoCredentialsError:
                return "Error: S3へのアクセス権がありません。"

    return render_template('upload.html')





@app.route('/view/<string:pdf_id>')
def view(pdf_id):
    try:
        response = table.get_item(Key={'id': pdf_id})
        file_name = response['Item']['file_name']
        presigned_url = s3.generate_presigned_url(
            'get_object',
            Params={'Bucket': S3_BUCKET, 'Key': file_name},
            ExpiresIn=3600
        )    
        pdf_data = requests.get(presigned_url).content
        return Response(pdf_data, content_type='application/pdf')
    except KeyError:
        return "Error: PDFが見つかりません。"

if __name__ == '__main__':
    app.run()

このコードでは、index()関数でアプリケーションのホーム画面、upload()関数でPDFアップロード機能、view()関数でアップロードされたPDFの表示機能をそれぞれ実装しています。また、S3およびDynamoDBの設定も行っています。 PDFデータをResponseオブジェクトに渡し、content_typeをapplication/pdfとして返します。これで、ブラウザでPDFファイルが表示されるようになります。

[templates/index.html]

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"content="width=device-width,initial-scale=1.0">
    <title>Image App</title>
</head>
<body>
    <h1>Welcome to the Image App</h1>
    <p><a href="{{ url_for('upload') }}">Uploadan image</a></p>

    <h2>Uploaded Images</h2>
    {% for image in images %}
        <p>
            <a href="{{ url_for('view',image_id=image.id) }}">Image: {{image.file_name }}</a>
        </p>
    {% endfor %}
</body>
</html>

[templates/update.html]

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Upload Image</title>
  </head>
  <body>
    <h1>Upload an Image</h1>
    <form action="{{ url_for('upload') }}" method="post" enctype="multipart/form-data">
      <p><input type="file" name="file"></p>
      <p><input type="submit" value="Upload"></p>
    </form>
    <p><a href="{{ url_for('index') }}">Back to Home</a></p>
  </body>
</html>

Zappaを使ってLambdaにデプロイ #

{
  "production": {
    "app_function": "app.app",
    "aws_region": "ap-northeast-1",
    "s3_bucket": "your-zappa-deploy-bucket",
    "project_name": "image-app",
    "runtime": "python3.8",
    "timeout_seconds": 30
  }
}

Zappaを使ってデプロイします。

$ zappa deploy production

ウェブアプリケーションのテスト #

デプロイが完了したら、表示されたURLにアクセスしてウェブアプリケーションをテストします。PDFをアップロードし、正常に表示されるか確認します。

以上で、Flask, DynamoDB, Lambda, S3, Zappaを使用してPDFを投稿・保存・表示するウェブアプリケーションの作成が完了しました。