Flaskで画像を可視化する簡単な Webアプリ

# はじめに

これまではDjangoを触ってきましたが、Flaskも触ってみようということで今回作成しました。そして今後は物体検出ができるWebアプリを作りたいので、その前段階として画像をアップできる簡易なWebアプリを作成することを決めました。

> 今回の目標：Falskに慣れるために単に画像をUploadできるアプリを作成する

# 完成品

実際に作成したところ次のようなものになりました。

![flask_gif01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552407/01475661-c4c6-5ac4-5ced-5263b6deb080.gif)


# 実行環境

使用したパッケージ等のバージョンは以下の通りです。

> python 3.8.5
> flask  1.1.2

# 作成したアプリについて

### ディレクトリ構造

まず、ディレクトリ構造は以下の通りです。

```text:ディレクトリ構造図
myProject
├── app.py
├── static
│   └── media（画像がここに保存される）
└── templates
    ├── base.html
    └── index.html
```

### コード

次にコードです。

```python:app.py
from pathlib import Path
from werkzeug.utils import secure_filename

from flask import Flask
from flask import render_template, redirect, send_from_directory, request

BASE_DIR = Path(__file__).resolve().parent
UPLOAD_FOLDER = BASE_DIR / "static" / "media"
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}

app = Flask(__name__, static_folder="static")
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER


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


@app.route('/', methods=["GET", "POST"])
def index():
    # トップページ
    return render_template("index.html", images=[p.relative_to(BASE_DIR) for p in list(UPLOAD_FOLDER.glob('*.*'))])


@app.route('/images/<path>')
def send_js(path):
    # 画像ページ
    return send_from_directory(app.config["UPLOAD_FOLDER"], path)


@app.route('/upload', methods=["POST"])
def upload():
    if 'image' not in request.files:
        # ファイルが選択されていない場合
        print('ファイルが選択されていません')
        return redirect('/')

    img = request.files['image']
    # 画像として読み込み

    if img.filename == "":
        # ファイル名がついていない場合
        print("ファイル名がありません")
        return redirect('/')

    if img and allowed_file(img.filename):
        filename = secure_filename(img.filename)

        try:
            img.save(UPLOAD_FOLDER / filename)
            # 画像を保存
        except Exception as e:
            print(f"画像の保存に失敗: {e}")

        return redirect('/')


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

templateファイルは以下の通りです。

```html:base.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>画像処理</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
```

```html:index.html
{% extends "base.html" %}
{% block content %}
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="image" accept="image/png, image/jpeg">
        <button type="submit">submit</button>
    </form>
    {% if images %}
        {% for path in images %}
            <div>
                <img src="../{{ path }}" style="margin-top: 10px; vertical-align: bottom; width: 128px;"
                     alt="image">
                <br>
                <p>{{ path }}</p>
            </div>
        {% endfor %}
    {% endif %}
{% endblock %}
```

index.htmlは後に拡張しやすい様にbaseをベースにして作成しました。

# ハマったところ

#### 画像pathの取り出し方
絶対パスや相対パスは頭で考えてもわからないのでpathを色々試して解決しました。

#### static_floderの設定

はじめはapp.pyにおいてstatic_folderを宣言していませんでしたが、宣言をしたことによって画像が表示される様になりました。


>x: app = Flask(__name__)
>
>◯: app = Flask(__name__, static_folder="static")

# 参考文献

参考にした文献は次の通りです。

* [ファイルのアップロード（Flask公式サイト）](https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/patterns/fileuploads.html)
* [Flaskで簡単につくる、画像処理した結果を見るだけのWebサービス](https://qiita.com/redshoga/items/60db7285a573a5e87eb6)
* [flask htmlにて画像を表示したい](https://teratail.com/questions/244601)


以上でこの記事は終わりです。ここまでお読みいただきありがとうございました。