16
8

More than 3 years have passed since last update.

【Python】REST API でファイルをアップロードする (FastAPI)

Last updated at Posted at 2021-02-07

はじめに

FastAPIで

これまで WEB APIは Flaskで作ってきましたが、調べているとfastAPIを使う方が、便利なようです。非同期処理がどうたらとかいろいろあるようですが、個人的に一番響いたのは、http://localhost:8080/docs と開くと、自作のWEB APIのマニュアルやテストができてしまうことです。これはもうチュートリアルをたどるとすぐできるので、書くのは省略するか後回し。これに感動しました。

今回、FastAPIでファイルをアップロードして保存する方法を少し調べたのでメモしておきます。

ファイルのアップロード

Request File (tutorial)

Tutorial に書いてあることのコピーなのですが、まぁ自分のメモです。
FastAPI Tutorial-User Guide の Request File のページです。

main.py

from fastapi import FastAPI, File, UploadFile, Form

app = FastAPI()

@app.post("/files/")
async def create_file(file: bytes = File(...)):
    return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
    return {"filename": file.filename}

下記のコードはtutorial にあるものです。私の環境では下記で動きました。bytes や UploadFileを引数にすれば良さそうです。

python3 -m pip install uvicorn, fastap
python3 -m uvicorn main:app --reload

上記のサーバを起動中に、適当に同じディレクトリにある16M bytes 位のファイルを指定してみると、

$ curl -X POST "http://127.0.0.1:8000/files/" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -F "file=@archive.zip;type=application/x-zip-compressed"
{"file_size":16957046}

と返ってきました。

一時ファイルの保存

UploadFile の file をshutil.copyfileobj()すれば良いようです。
ここに一時ファイルに保存する例があったので、動かしてみたら動きました。
https://github.com/tiangolo/fastapi/issues/426

main2.py
from fastapi import FastAPI, File, UploadFile, Form
import shutil
from pathlib import Path
from tempfile import NamedTemporaryFile

@app.post("/saveuploadfile/")
async def save_upload_file_tmp(fileb: UploadFile=File(...), token:str=Form(...)):
    tmp_path:Path = ""
    try:
        print(type(fileb))# <class 'starlette.datastructures.UploadFile'>
        print(type(fileb.file)) #<class 'tempfile.SpooledTemporaryFile'>
        suffix = Path(fileb.filename).suffix
        with NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
            shutil.copyfileobj(fileb.file, tmp)
            tmp_path = Path(tmp.name)
            print(tmp_path)
    finally:
        fileb.file.close()
    return {
        "filename": fileb.filename,
        "temporary_filepath": tmp_path,
        "token": token,
        "fileb_content_type": fileb.content_type,
    }

別ターミナルで動かすと、

$ curl -X POST "http://127.0.0.1:8000/saveuploadfile/" -H  "accept: application/json" -H  "Content-Type: multipart/form-data" -F "token=agd" -F "fileb=@archive.zip;type=application/x-zip-compressed"

responce body が

{
  "filename": "archive.zip",
  "temporary_filepath": "/tmp/tmp2ki9kvdp.zip",
  "token": "agd",
  "fileb_content_type": "application/x-zip-compressed"
}

と返ってきます。

まとめ

とりあえずfastAPIでファイルのアップロードはできた。明日の作業も何とか進められそうだ。

まだまだ試せていないのは以下の通り。

  • fileのサイズの取得方法が分からない。一応、tempfile(https://docs.python.org/3/library/tempfile.html) を読んではみたのだが。
  • 複数ファイルの取り扱い。これはTutorial にあったので、試せばよいだけだと思うが。
  • ダウンロードもしたい。

(2021/02/08)

16
8
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
16
8