概要
Python Flaskで構築されたWebアプリケーションにおいて、アップロードされたファイルが想定しているContent-Typeのファイルなのかチェックしたい場合があります。
その際には、アップロードされたファイルのMIMEタイプを識別してバリデーションを行うことが一般的です。
Python Flaskで構築されたWebアプリにおいて、MIMEタイプを識別する主な方法は以下が挙げられます。
- Flaskのファイルアップロード
- Python標準ライブラリのmimetypes
- 外部ライブラリのpython-magic
本記事では、それぞれの懸念点や導入時に困ったことについて記載します。
そもそもMIMEタイプとは
MIMEタイプとは、メールやWEBアプリのデータ転送の際にデータの種類を識別するためのコードです。
これらのコードは「タイプ名/サブタイプ名」の形式で表されます。
(参考)MIMEタイプって何者?
Flaskのファイルアップロード
Flaskでは、ファイルアップロード時に自動的にMIMEタイプを識別してくれる機能があります。
Flaskアプリケーションのテスト
しかし、環境によってはファイルの種類が不明のapplication/octet-streamとなることがありました。
これについては原因は不明ですが、不明なMIMEタイプだとバリデーションで弾かれるため注意が必要です。
Python標準ライブラリのmimetypes
mimetypesはPythonの標準ライブラリのため、外部ライブラリに依存せずに実装できるので手軽です。
ただし、mimetypesはファイルの拡張子部分の文字列のみでMIMEタイプを識別するため、拡張子を意図的に変更したファイルなどは正しく検知できません。
そのため、ファイルの中身でMIMEタイプを識別したい場合はこの方法は不適切です。
外部ライブラリのpytyon-magic
python-magicはファイルの中身からMIMEタイプを識別することができるため、今回の要件には最も適していました。
今回はDockerコンテナ内でWebアプリケーションを実装したのですが、実際に動かしてみると以下のエラーが出力されてコンテナがrestartを繰り返してしまいました。
ImportError: failed to find libmagic. Check your installation
python-magicはlibmagicのラッパーなので、事前にOS側にlibmagicがインストールされている必要があるようです。
ちなみにpython:3.9.7-buster
にはデフォルトでインストールされているようですが、今回採用したpython:3.9.7-slim-buster
にはインストールされていませんでした。
Dockerfileにlibmagicのインストールコマンドを追記することで、python-magicを使用してアップロードされたファイルのMIMEタイプを正しく検知することができました。
サンプルコード
以下はpython-magicの動作を確認するための簡易的なコードになります。
Flask==2.1.0
python-magic==0.4.24
FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt ./
RUN apk add --no-cache file && \
pip install --no-cache-dir -r requirements.txt
COPY app.py ./
COPY templates ./templates
EXPOSE 5000
CMD ["python", "app.py"]
from flask import Flask, render_template, request
import magic
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload_file():
# リクエストからファイルを取得する
file = request.files['file']
# ファイルタイプを判定する
file_type = magic.from_buffer(file.read(), mime=True)
# ログに出力する
app.logger.info('File type: %s', file_type)
return file_type
if __name__ == '__main__':
app.run(host="0.0.0.0")
<html>
<body>
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" />
<input value="upload" type="submit"/>
</form>
</body>
</html>
以下が上記のコードを実際に実行した際の結果になります。
下記コードを実行して、コンテナを起動してください。
docker build -t sample .
docker run -p 5000:5000 sample
ブラウザからhttp://(IPアドレス):5000にアクセスし、ファイルを選択して[upload]をクリックすると、選択したファイルのMIMEタイプが出力されます。
まとめ
この記事は、Python FlaskでWebアプリケーションを構築し、アップロードされたファイルのMIMEタイプを識別してバリデーションする方法について述べました。
Flaskのファイルアップロード、Python標準ライブラリのmimetypes、外部ライブラリのpython-magicについて説明し、それぞれの懸念点や導入時に困ったことについて記載しています。