flaskとS3をつなげ、写真をアップロードする【Python】【コピペ可】

先日、S3へのAPIを紹介しましたが、

今回は「flaskを用いて、HTMLからファイルをアップロードする」ところを作成しました。

S3への接続の仕方は、他のサイトでもたくさん紹介していますが、そこからflaskを用いてどう応用すればよいか、の一歩先に進んだところを解説します。

今回作成するページ

今回の実装は、外観的には以下2つです。

  1. 画像をアップロードする
  2. アップロードした画像を表示する

(上の画像は、アップロードした画像をページに表示している状態です。)

では、さっそくコードを見ていきましょう。

全体像から見た解説順番

ここから、app.pyの処理の中身を見ていくわけですが、処理の流れとしては以下のようになります。

全体の流れ

①HTMLから画像ファイルを取得

②画像ファイルのチェック

③画像をS3へアップロード。パスを返す。

なので、コードもこの順で見ていきます。

なお、import文などは、後の全コードに合わせて載せますので、そちらで見ていただければと思います。

ではコードを見ていきましょう。

HTMLから画像ファイルを取得

コード

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/regist_product', methods=['POST'])
def regist_product():
    temp_file = request.files.get('upfile',None)
    #if temp_file is None: return <ファイルがなかった時の挙動>
    S3_file = file_upload(temp_file)
    #if ok is False: return <ファイルアップロード失敗時の挙動>
    return render_template('index.html',S3_file=S3_file)

解説

temp_file = request.files.get('upfile',None)

requestで、ファイルを取得します。

HTMLには側には、enctype属性を指定するのを忘れないようにしましょう。

requestの他の使い方は以下でまとめています。

    S3_file = file_upload(temp_file)
    return render_template('index.html',S3_file=S3_file)

後述しますが、file_upload関数で、temp_fileを渡しチェックを行ってから、アップロードを行います。

ファイルをチェックしたり、アップロードしたりのコードはなるべく分けて書くようにしましょう。

分けて書くことで、これ以外の部分で画像アップロードがしたいときに、使うことができますし、別のコードを書く時にも関数をコピペするのみで行けます。

自分のライブラリを増やすように書いていきましょう。

file_upload関数のレスポンスでは、S3でのファイルのパスが返されますので、それをHTMLに返すようにします。

画像ファイルのチェック

コード

#保存先にディレクトリとURLの指定
IMAGES_DIR = './static/images'

def file_upload(temp_file):
    if not (temp_file):
        return False
    #ファイル形式を判定する。
    fmt = imghdr.what(temp_file)
    if fmt != 'jpeg':
        text = 'jpeg以外アップロードできません。'
        return False
    #保存先のファイル名を決める
    time_s = 'test_' + uuid.uuid4().hex
    fname = time_s + '.jpg'
    #一時ファイルを保存先ディレクトリに保存
    dir = IMAGES_DIR + '/' + fname
    temp_file.save(dir)
    S3_filepath = upload(dir,fname)
    os.remove(dir)
    return S3_filepath

解説

    if not (temp_file):
        return False
    #ファイル形式を判定する。
    fmt = imghdr.what(temp_file)
    if fmt != 'jpeg':
        text = 'jpeg以外アップロードできません。'
        return False

ファイルない場合ははじくようにします。

また、今回はjpeg以外は、アップロードできないようにする(特に理由はない)のでimghdrメソッドで拡張子を区別しています。

    time_s = 'test_' + uuid.uuid4().hex
    fname = time_s + '.jpg'

ここでは、画像のファイル名を決めています。

S3へアップロードするためなので、一意の名前を付けて区別します。

今回はuuidで名前を付けることにしました。

    dir = IMAGES_DIR + '/' + fname
    temp_file.save(dir)

S3へアップロードするために、一旦画像をローカルに保存します。

保存しなくても直接渡す方法もあるようですが、複雑になりそうなのでここでは割愛します。

    S3_filepath = upload(dir,fname)
    os.remove(dir)
    return S3_filepath

後述する、upload関数に画像ファイルのローカルパスと、画像のファイル名を渡します。

レスポンスは、S3のファイルパスになっているので、そのまま返します。

S3へファイルをアップロード

コード

load_dotenv()
client = boto3.client('s3')

bucket = '<バケットの名前を入れる>'
objecturl = '<画像のURLのバケットの前の部分を入れる>'

def upload(file_path,filename):
    Filename = file_path
    Bucket = bucket
    Key = 'test/' + filename
    response =client.upload_file(Filename, Bucket, Key)
    print('S3_upload_response:',response)
    return objecturl + Key

解説

bucket = '<バケットの名前を入れる>'
objecturl = '<画像のURLのバケットの前の部分を入れる>'

バケット名はAWSで決めたものをそのまま入力してください。

objecturlは、画像ファイルのをタップすると「オブジェクトの概要」があり、その中の「オブジェクトURL」のバケットより前の部分を入力します。

おそらく以下のようになっているので、前の部分をコピーしましょう。

<この部分>/バケット名/画像ファイル名

def upload(file_path,filename):
    Filename = file_path
    Bucket = bucket
    Key = 'test/' + filename
    response =client.upload_file(Filename, Bucket, Key)
    print('S3_upload_response:',response)
    return objecturl + Key

S3に画像をアップロードするためには、「ローカルのファイルパス」と「バケット名」「バケット内の保存したいパス」の3つが必要です。

それらを.upload_fileの引数に入れればアップロードができます。

※アップロードのためには、アクセスキーが必要になります。.envファイルの中にしまうのがセキュリティ上よいので、以下ページを参考にしてください。

全コード

import os ,uuid
from flask import Flask,render_template, request
import imghdr
import boto3
from dotenv import load_dotenv

#Flaskインスタンスと暗号キーの指定
app = Flask(__name__)
app.secret_key = 'TIDe5adlkfnvorh034n'

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/regist_product', methods=['POST'])
def regist_product():
    temp_file = request.files.get('upfile',None)
    #if temp_file is None: return <ファイルがなかった時の挙動>
    S3_file = file_upload(temp_file)
    #if ok is False: return <ファイルアップロード失敗時の挙動>
    return render_template('index.html',S3_file=S3_file)

#保存先にディレクトリとURLの指定
IMAGES_DIR = './static/images'

def file_upload(temp_file):
    if not (temp_file):
        return False
    #ファイル形式を判定する。
    fmt = imghdr.what(temp_file)
    if fmt != 'jpeg':
        text = 'jpeg以外アップロードできません。'
        return False
    #保存先のファイル名を決める
    time_s = 'test_' + uuid.uuid4().hex
    fname = time_s + '.jpg'
    #一時ファイルを保存先ディレクトリに保存
    dir = IMAGES_DIR + '/' + fname
    temp_file.save(dir)
    S3_filepath = upload(dir,fname)
    os.remove(dir)
    return S3_filepath



load_dotenv()
client = boto3.client('s3')

bucket = '<バケットの名前を入れる>'
objecturl = '<画像のURLのバケットの前の部分を入れる>'

def upload(file_path,filename):
    Filename = file_path
    Bucket = bucket
    Key = 'test/' + filename
    response =client.upload_file(Filename, Bucket, Key)
    print('S3_upload_response:',response)
    return objecturl + Key

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

まとめ

今回は、flaskでS3への接続を試しました。

以下の記事では、flaskでmongoDBへの接続を行っています。併せて読んでみてください。

(Visited 58 times, 1 visits today)