0
1

More than 1 year has passed since last update.

FlaskでアップロードされたファイルのMIMEタイプを識別しようとしたら困った話

Last updated at Posted at 2023-04-12

概要

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の動作を確認するための簡易的なコードになります。

./requirements.txt
Flask==2.1.0
python-magic==0.4.24
./Dockerfile
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"]
./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")
./templates/index.html
<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タイプが出力されます。
image.png
image.png

まとめ

この記事は、Python FlaskでWebアプリケーションを構築し、アップロードされたファイルのMIMEタイプを識別してバリデーションする方法について述べました。
Flaskのファイルアップロード、Python標準ライブラリのmimetypes、外部ライブラリのpython-magicについて説明し、それぞれの懸念点や導入時に困ったことについて記載しています。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1