2020/06/26

FastAPI + Nuxt.jsでファイルのダウンロード

nuxtjsfastapi

概要

サーバー側がFastAPIベースで、クライアント側がNuxt.jsベースのアプリで

Nuxt.jsの画面上のボタンクリックで、サーバー側にファイルのリクエストを行い、ダウンロードするように実装しました。

実装内容

サーバー側

デフォルメすると以下のようになります。

  • /download/ というpathでリクエストを受け付けると、リクエスト内で、一時ディレクトリにtsvファイルを作成して、それをそのまま返却しています。
  • fastapi.responses.FileResponse というclassを使うと簡単でした
import datetime
import csv
from pathlib import Path
from fastapi import APIRouter
from fastapi.responses import FileResponse

router = APIRouter()


@router.get("/download/")
def download():
    now = datetime.datetime.now()
    file_dir = "/path/to/output/dir"
    Path(file_dir).mkdir(parents=True, exist_ok=True)
    file_name = f"my_file_{now.strftime('%Y%m%d%H%M%S%f')}.tsv"
    tsv_file_path = Path(file_dir) / file_name
    with open(tsv_file_path, 'x') as f:
        writer = csv.writer(f, delimiter="\t")
        header = ["a", "b", "c"]
        writer.writerow(header)
        writer.writerow([1, 2, 3])
        writer.writerow([4, 5, 6])
    return FileResponse(tsv_file_path, filename=file_name, media_type="text/tab-separated-values")

クライアント側

<template>
  <button @click="download">DOWNLOAD</button>
</template>

<script>
export default {
  methods: {
    async download() {
      await this.$axios.$get('/download/', {
        responseType: 'blob'
      })
      .then((response) => {
        const blob = new Blob([response], {
          type: 'text/tab-separated-values'
        })
        const fileName = 'my_file.tsv'
        if (window.navigator.msSaveOrOpenBlob) {
          // for IE, Edge
          window.navigator.msSaveOrOpenBlob(blob, fileName)
        } else {
          // for Chrome , etc...
          const url = window.URL.createObjectURL(blob)
          const link = document.createElement('a')
          link.href = url
          link.setAttribute('download', fileName)
          document.body.appendChild(link)
          link.click()
          window.URL.revokeObjectURL(url)
          link.parentNode.removeChild(link)
        }
      })
      .catch((err) => {
        console.log(err)
      })
    }
  }
}
</script>

やや axios経由のファイルダウンロードだと癖がありましたが、axiosで実現できたので、認証処理などが流用できて素敵でした

以上です。