flaskとS3をつなげ、写真をアップロードする【Python】【コピペ可】
先日、S3へのAPIを紹介しましたが、
今回は「flaskを用いて、HTMLからファイルをアップロードする」ところを作成しました。
S3への接続の仕方は、他のサイトでもたくさん紹介していますが、そこからflaskを用いてどう応用すればよいか、の一歩先に進んだところを解説します。
目次(クリックで読みたい部分にジャンプできます)
今回作成するページ
今回の実装は、外観的には以下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への接続を行っています。併せて読んでみてください。