GoogleドライブのファイルをGoogle Cloud Storageへコピーする
Google Cloud APIの中には、gs://[bucket]/[path]
の形式になっているCloud StorageのURLを与えることができるものがある。今回はGoogleドライブに入っているファイルをCloud Storageにコピーしてgs://
のURLとして使えるようにしたかった。
streamをうまく使うことで、一時ファイルを作らずにコピーすることができた。
準備
以下のパッケージをインストールしておく。
$ npm i googleapis @google-cloud/storage
ドライブ用にGoogle APIのOAuthクライアントIDを作成する。APIとサービスの認証情報から[OAuth クライアント ID の作成]で作成できる。コマンドラインツールの場合は[その他]を選んで作成する。JSONをダウンロードしておく。
Cloud Storage用にサービスアカウントキーを発行して、これもJSONをダウンロードしておく。
Googleドライブの認証
ドライブ公式のクイックスタートをまとめ直したクラスを作った。
https://gist.github.com/iseebi/4d21bf68c79bc020ea3515693676a35f
コピー関数
こんな感じの関数を書いた。
import {Bucket} from "@google-cloud/storage"; import {drive_v3} from "googleapis"; import {Readable} from "stream"; import Drive = drive_v3.Drive; async function copyDriveToStorage(drive: Drive, bucket: Bucket, driveFileId: string, uploadFileName: string) { const meta = await drive.files.get({fileId: driveFileId, supportsTeamDrives: true, fields: 'mimeType'}); const media = await drive.files.get({fileId: driveFileId, supportsTeamDrives: true, alt: 'media'}, {responseType: 'stream'}); const uploadFile = bucket.file(uploadFileName); await new Promise<void>((resolve, reject) => { const downloadStream = media.data as Readable; const uploadStream = uploadFile.createWriteStream({ metadata: { cacheControl: 'no-cache', contentType: meta.data.mimeType, } }); downloadStream.pipe(uploadStream); downloadStream.on('error', reject); uploadStream.on('error', reject); uploadStream.on('finish', resolve); }); }
ポイントはこのあたり。
drive.file.get
のparamsにalt: 'media'
、optionsにresponseType: 'stream'
を指定- その戻り値の
data
をReadableにキャストする → 読み込み用のストリーム bucket.file
でcreateWriteStream
→ 書き込み用のストリーム- 読み込み用のストリームから、書き込み用のストリームに
pipe
する - 書き込み用のストリームがfinishしたら完了、どちらかのストリームがエラーを返したらreject
今回実際に使ったものでは、mimeTypeを事前に絞り込んでいたので、metaの取得はせずにハードコーディングした。
呼び出し
こんな感じで使う。
import {Storage} from "@google-cloud/storage"; import {Authenticator} from "./authenticator"; import {google} from "googleapis"; const auth = await authenticator.authenticateAsync(); const drive = google.drive({version: 'v3', auth}); const storage = new Storage({projectID:'00000000000', keyFilename: 'service_account.json'}); const bucket = storage.bucket('my-bucket-name'); const fileID = '......'; const destination = 'filename'; await copyDriveToStorage(drive, bucket, fileID, destination);