6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS S3からファイルを取得したFlask APIがファイルをレスポンスとして返戻し、Vueでダウンロードする

Last updated at Posted at 2020-07-01

はじめに

忙しいタイトルをしていて、大変申し訳ありません。
現在、以下のアプリ構成でお仕事をしています。
フロントエンド: Vue.js
バックエンド: Flask (python)

Flaskの方については、これまで全く触ったことがなかったので、新鮮さを噛みしめつつ、日々触っています。
そもそもpythonの方も、ファイルを読み込んでcsvを吐き出してくれるというプチツールを作ったことがあるぐらいですので、多くのことを勉強させてもらっています。

本記事で書くこと

タイトルそのままですが、以下を実現します。

  1. Flask(Api)が、AWS SDK for Python (Boto3)を使って、AWS S3からファイルを取得する。
  2. 取得したファイルデータをレスポンスに詰め、クライアント(Vue.js)に返却する。
  3. クライアント(Vue.js)がレスポンスからファイルをダウンロードする。
    Lambda + API GateWayで良いのでは・・・
    こんなこともできるんだというぐらいの気持ちで読んでください。

本記事で書かないこと

  • Lambda + API GateWayで上記の流れを実現すること
  • Flaskについてのあれこれ
  • pythonについてのあれこれ
  • aws関連についてのあれこれ
  • Vue.jsについてあれこれ
  • fetch APIのあれこれ

おそらく上記は色々な方が実践済みだと思うので。

なぜやろうと思ったのか

そもそも、今回の機能は amplify で実現する予定でした。
が、「Internet Explorer」の場合は、なぜか、aws credentialの認証情報がamplifyにわたってくれず、s3からファイルを取得しようしたときに、403エラーになるという状況に・・・
そのため、このように実装することにしました。
ですので、「Internet Explorer」を対象外とする場合は、素直にamplifyを使うことをオススメします・・・

環境

  • vue: 2.6.11
  • boto3: 1.12.38
  • Flask: 1.1.2
  • python: 3系

事前準備

  • awscli、boto3をインストールしておく
  • aws configureで、awscliを使えるようにしておく

それでは内容へ

1. Flask(Api)が、AWS SDK for Python (Boto3)を使って、AWS S3からファイルを取得する。

s3からファイルを取得する場合に、以下二つの方法があります。

ローカルにダウンロード
boto3.resource('s3').Bucket(BUCKET).download_file(Filename=KEY, Key=KEY)
メモリ上にダウンロード
boto3.client('s3').get_object(Bucket=bucket, Key=key)

Flaskのローカル(アプリケーション上)にファイルがダウンロードされてしまうと、そのファイルを消したりしないといけないのが面倒なので、後者のメモリ上にダウンロードする方法を採用しました。

ソースコード

s3からファイルを取得する
@app.route('/file', methods=['GET'])
def get_file_from_s3():
    s3 = boto3.client('s3')
    bucket = 'YOUR_BUCKET_NAME'
    key = 'YOUR_FILE_NAME'

    obj = s3.get_object(Bucket=bucket, Key=key)

これで、/fileのエンドポイントを叩くことで、s3からファイルオブジェクトを取得することができました。
このobjの内、Bodyのバイト配列を今回は利用します。

2. 取得したファイルデータをレスポンスに詰め、クライアントに返却する。

それでは、取得したファイルオブジェクトからレスポンスをつくりましょう。
レスポンスはmake_response()でつくります。

ソースコード

取得したファイルオブジェクトからレスポンスをつくって返却してあげる
@app.route('/file', methods=['GET'])
def get_file_from_s3():
    s3 = boto3.client('s3')
    bucket = 'YOUR_BUCKET_NAME'
    key = 'YOUR_FILE_NAME'

    obj = s3.get_object(Bucket=bucket, Key=key)
    
    response = make_response()

    # レスポンスobjectのdataに取得したファイル情報を設定します。
    # read()で、ファイルを読み込みます。
    response.data = obj['Body'].read()

    # ダウンロード時のファイル名を定義します。
    # quoted_filenameでURLエンコードします。また、日本語のファイル名でもできるように、UTF-8エンコーディングもします。
    quoted_filename = urllib.parse.quote(name)
    response.headers['Content-Disposition'] = "attachment; filename='{}'; filename*=UTF-8''{}".format(quoted_filename, quoted_filename)

    return response

3. クライアントがレスポンス情報をダウンロードする。

それでは、クライアント(Vue.js)でレスポンスのファイル情報をダウンロードしましょう。
今回はFetch APIを利用します。

ソースコード

レスポンスからファイルをダウンロードする
    fetch('http://XXXXXX/file', {
      method: 'GET',
    }).then((response) = >{
      // blob()で、Blob形式にします
      return response.blob();
    }).then((blob) => {
      // IEの場合は、msSaveBlobを使います
      if (window.navigator.msSaveBlob) {
        window.navigator.msSaveBlob(blob, 'ファイル名.拡張子');
      } else {
        const a = document.createElement('a');
        a.download = 'ファイル名.拡張子';
        a.href = URL.createObjectURL(blob);
        a.click();
      }
    })

これで、ファイル名.拡張子のファイルをダウンロードできます。
ファイル名.拡張子Content-Dispositionのfilenameとしたい場合は、
**response.headers.get('Content-Disposition)**でContent-Dispositionを取得できるので、そこから抽出して使うと良いと思います。

参考にさせていただいた記事

Flaskでファイルダウンロードを実現する3つの方法
Python の boto3 で S3 とダウンロード/アップロードする
Djnagoメモ Content-Dispositionのfilenameに日本語をセットする

6
1
1

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
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?