第2回 - Web API と Web UI でファイルダウンロード(Web UI 編)

## はじめに
前回からファイルをダウンロードする簡単な Web API と Web UI のサンプルを紹介しています。
[第1回目](https://qiita.com/ponko2bunbun/items/71e3c00f82cbaa18218e)の前回は [Node.js](https://nodejs.org/en/) + [Express.js](http://expressjs.com/) での Web API を紹介しました。
第2回目の今回は [Vue.js](https://jp.vuejs.org/index.html) + [Vuetify](https://vuetifyjs.com/ja/) で簡単な Web UI を作り、前回の Web API を叩いて実際にファイルをダウンロードします。

1. [Web API 編](https://qiita.com/ponko2bunbun/items/71e3c00f82cbaa18218e)
1. Web UI 編(←イマココ)

## 環境
OS: macOS Mojave 10.14.6
Vue.js: 2.6.10
Vuetify: 2.1.0
[axios](https://github.com/axios/axios): 0.19.0
[file-saver](https://github.com/eligrey/FileSaver.js): 2.0.2

## Download.vue
```vue:Download.vue
<template>
    <v-container>
        <v-row>
            <v-col cols="2">
                <v-btn
                    v-on:click="click('sample.csv')"
                    color="primary"
                    block
                >
                    Download csv
                </v-btn>
            </v-col>

            <v-col cols="2">
                <v-btn
                    v-on:click="click('sample.png')"
                    color="primary"
                    block
                >
                    Download image
                </v-btn>
            </v-col>
        </v-row>
    </v-container>
</template>

<script>
import axios from 'axios'
import urljoin from 'url-join'
import { saveAs } from 'file-saver'

export default {
    methods: {
        async click (fileName) {
            const response = await axios.get(urljoin('http://localhost:3000/api/download'), {
                responseType: 'blob',
                params: {
                    'file': fileName
                }
            }).catch((error) => {
                console.log(error)
            })

            if (response && (200 == response.status)) {
                const contentDisposition = response.headers["content-disposition"]
                const contentDispositions = contentDisposition.split(';')
                const fileNames = contentDispositions[contentDispositions.length - 1].split('=')
                fileName = fileNames[fileNames.length - 1]
                saveAs(response.data, fileName)
            }
        }
    }
}
</script>
```
ボタンを2つ配置しただけのシンプルな UI です。
ダウンロードするファイルは CSV と PNG の2種類です。
ボタンをクリックすると対応するファイル名(sample.csv or sample.png)を引数にして Web API を実行します。
Web API 実行後、自動的にファイルを保存するようになっています。

### Web API実行
```vue:Download.vue(抜粋)
    const response = await axios.get(urljoin('http://localhost:3000/api/download'), {
        responseType: 'blob',
        params: {
            'file': fileName
        }
    }).catch((error) => {
        console.log(error)
    })
```

#### 解説
Web API の実行には axios を使用しています。
また、 `responseType: 'blob'` で受け取る型を Blob 型に指定します。
（後述の saveAs@file-saver で Blob 型のデータを指定するため）

### ファイル保存
```vue:Download.vue(抜粋)
    const contentDisposition = response.headers["content-disposition"]
    const contentDispositions = contentDisposition.split(';')
    const fileNames = contentDispositions[contentDispositions.length - 1].split('=')
    fileName = fileNames[fileNames.length - 1]
    saveAs(response.data, fileName)
```

#### 解説
Web API 側で `content-disposition` (ヘッダー)にファイル名を指定しており、保存時のファイル名に使用しています。
ファイルの保存は file-saver を使用します（とても便利）。
saveAs に Blob 型のデータとファイル名を指定すると自動でダウンロードが始まります。

## 動作確認
![ファイルダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/520557/d344af72-0028-976e-5db7-0a9082b6619f.gif)
第1回目のアプリケーションも合わせて起動します。
各ボタンの押下でファイルのダウンロードが始まっているのがわかります。

## まとめ
2回に渡って Web API と Web UI でファイルダウンロードのサンプルを紹介しました。
複雑なコードを書かなくても、いくつかのライブラリを利用することで簡単に実装できました。
Web API でデータを取得し、保存用のモジュールでデータにデータを渡しているだけなので、アレンジもしやすいと思います。

今回、使用したコードはGitHubで公開しています。
https://github.com/ponko2bunbun/vuetify-file-download-sample