Node.jsでGoogle Drive上のファイルをダウンロードする (Google Drive API v3)


1年くらい前にGoogle Drive関連の記事を書いてたけど、久々に触りたくなったので調査再開。

* 参考記事
	* [Node.jsでGoogle Drive上のファイルを削除する (Google Drive API v3)](https://qiita.com/n0bisuke/items/a1b39635029242f31713)
	* [Node.jsでGoogle Drive上のファイルをリネームする (Google Drive API v3)](https://qiita.com/n0bisuke/items/dcc7be337a555b018b19)
	* [Node.jsでGoogle Drive上のファイルを指定フォルダに移動する (Google Drive API v3)](https://qiita.com/n0bisuke/items/8f108408a9e1067b9225)
	* [Node.jsでGoogle Drive上のファイルを複製（copy）する (Google Drive API v3)](https://qiita.com/n0bisuke/items/6d73bb6e1af123db9a9b)
	* [Node.jsでGoogle Driveの指定フォルダからファイル一覧を取得メモ (Google Drive API v3)](https://qiita.com/n0bisuke/items/ff1479cd14e7a0c0be0c)

メソッドは[Files: get](https://developers.google.com/drive/api/v3/reference/files/get)になります。

ライブラリの書き方だと`drive.files.get()`です。

## スコープ指定

スコープの指定は割と多め。

```
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
https://www.googleapis.com/auth/drive.readonly
https://www.googleapis.com/auth/drive.metadata.readonly
https://www.googleapis.com/auth/drive.appdata
https://www.googleapis.com/auth/drive.metadata
https://www.googleapis.com/auth/drive.photos.readonly
```

## ダウンロードする

[チュートリアル](https://developers.google.com/drive/api/v3/quickstart/nodejs)の`function listFiles`の箇所を書き換えて利用してみます。

> 機能的には`function dlFile`とかにリネームした方が良いでしょうが一旦動かす体なのでスルー

`fileId`にGoogle DriveのファイルのIDを指定しましょう。

また`test.mp4`としてますが、ファイルの種類によって拡張子は変えてください。

```dj.js
async function listFiles(auth) {
    const drive = google.drive({version: 'v3', auth});
    const fileId = `xxxxxxxxxxxx`; //GoogleDriveのファイルIDを指定
    const dest = fs.createWriteStream('test.mp4', 'utf8');
    try {
        const res = await drive.files.get({fileId: fileId, alt: 'media'}, {responseType: 'stream'});
        res.data.on('data', chunk => dest.write(chunk));
        res.data.on('end', () => dest.end()); //保存完了
    } catch (err) {
        console.log('The API returned an error: ' + err);
    }
}
```

けっこうシンプルに書けますね。
実行すると手元に`test.mp4`が保存されます。

### おまけ: プログレス表示

[公式サンプル](https://github.com/googleapis/google-api-nodejs-client/blob/master/samples/drive/download.js)を参考にして書くとこんな感じになります。

```dl.js
//省略

async function listFiles(auth) {
    const drive = google.drive({version: 'v3', auth});
    const fileId = `xxxxxxxxxxxx`; //GoogleDriveのファイルIDを指定

    const dest = fs.createWriteStream('test.mp4', 'utf8');
    let progress = 0;

    try {
        const res = await drive.files.get({fileId: fileId, alt: 'media'}, {responseType: 'stream'});
        
        res.data.on('end', () => console.log('Done downloading file.'))
            .on('error', err => console.error('Error downloading file.'))
            .on('data', d => {
                progress += d.length;
                if (process.stdout.isTTY) {
                    process.stdout.clearLine();
                    process.stdout.cursorTo(0);
                    process.stdout.write(`Downloaded ${progress} bytes`);
                }
            })
            .pipe(dest);    
    } catch (err) {
        console.log('The API returned an error: ' + err);
    }
}
```

* 実行

こんな感じでプログレス表示があって良いですね。

```bash
$ node dj.js
Downloaded 79767428 bytesDone downloading file.
```

## 所感

実際に使うときには、

* 1. `drive.files.list()`でフォルダ内のファイル一覧及びIDなど取得
* 2. `drive.files.get()`でダウンロード

みたいな流れが多いかなぁと思います。

ファイル保存でStreamを使うあたりがNode.jsっぽさを感じますね。

> 参考: [Node.js Stream を使いこなす](https://qiita.com/masakura/items/5683e8e3e655bfda6756)