Googleドライブ上のファイルを更新する

概要

ファイルのバックアップなんかにも便利なGoogleドライブ。

ドライブへのアップロードを自動化するためのプログラム(の作り方)を紹介しようと思います。Drive APIを使って策っと作れます。今回はPythonで実装します。

準備

APIの有効化

GoogleのAPIを使うにはGoogle API Consoleで有効化する必要があります。

認証情報としてOAuth 2.0 クライアント IDを登録し、その情報をファイル(JSON形式)としてダウンロードします。やたら長い名前のファイルですが、credentials.jsonという名前にして保存して後で使います。

パッケージのインストール

Googleのドキュメントにもありますが、GoogleのAPIに必要なパッケージをpipでインストールします。

pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

コード解説

パッケージのインポート

GoogleのAPIを使うのに必要なパッケージをインポートします。

import pickle
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.http import MediaFileUpload
from google.auth.transport.requests import Request

その他にも使いそうなものもインポートします。

import sys
import os
import mimetypes

スコープ

APIを使う際のスコープ(権限)を指定します。ここではドライブ上のファイルに対する操作の権限を指定します。

SCOPES = ['https://www.googleapis.com/auth/drive.file']

認証

認証情報はローカルファイルに保存することができます。ここではGoogleの例に倣ってtoken.pickleという名前ということにします。すでに後述する認証が行われて入れば、その際の情報を利用します。

if os.path.exists('token.pickle'):
    with open('token.pickle', 'rb') as token:
        creds = pickle.load(token)

認証情報がない場合や認証情報が無効な場合は、シークレットファイル(credentials.json)を用いて認証を行います。
先に定義したスコープはここで使います。もしスコープを変更したければ、以前のtoken.pickleを削除して認証をやりなおせば、新しいスコープでの認証情報を作ることができます。

if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
        creds = flow.run_local_server(port=0)
    with open('token.pickle', 'wb') as token:
        pickle.dump(creds, token)

このコードを実行するとコンソール上にURLが表示されるので、そのURLにブラウザーでアクセスします。表示されたページでGoogleのログインとAPIの許可をすれば、コードの実行が再開されます。ここでログインしたユーザーで以後はAPIによる操作が行われます。

認証情報が正しく取得出来たらAPIを使うためのインスタンスを生成します。

service = build('drive', 'v3', credentials=creds)

アップロードするファイルの準備

ここからは実際のファイルの更新に関する処理です。説明のために、file_nameにドライブ上のファイルの名前、file_pathに更新に用いるローカルファイルのパスが設定されているものとします。

まずはアップロードするファイル(を指すオブジェクト)を用意します。ここではMimeTypeを便宜的にapplication/octet-streamとしていますが、できればファイルの形式に合わせて適切に指定した方がよいと思います。

media_body = MediaFileUpload(file_path, mimetype='application/octet-stream', resumable=True)

既存ファイルの確認

ドライブ上のフォルダーを指定します。ドライブ上のフォルダーはURLの/folders/より後の英数字の部分で識別されます。

folder_id = 'euchyG8uDTvWdmCsLkzU4zHbVn73BEUk'

例えば、ブラウザーで表示したときにURLが https://drive.google.com/drive/u/0/folders/euchyG8uDTvWdmCsLkzU4zHbVn73BEUk であれば euchyG8uDTvWdmCsLkzU4zHbVn73BEUk の部分がフォルダーのIDです。

そうしたらフォルダーに更新しようとするファイルが存在するか調べます。ドライブのAPIでは、上書きするのか別ファイルとして処理するのか、明示的に操作をする必要があります。

query = "name = '{}' and '{}' in parents and trashed=false".format(file_name, folder_id)
res = service.files().list(q=query).execute()

res'files'フィールドにフォルダーにする該当ファイルのリストが設定されます。len(res['files'])が0か否かでファイルが存在するかどうかを判断します。

アップロード

ファイルが存在しなければ、ドライブ上にファイルを作成します。

if len(res['files']) == 0:
    service.files().create(
            body={'name': file_name, 'mimeType': 'application/octet-stream', 'parents':[folder_id]},
            media_body=media_body,
    ).execute()

ドライブ上にすでに同名ファイルが存在する場合には更新をします。先ほどのファイルの確認で得た情報からファイルのidを取得して更新先を特定します。

else:
    service.files().update(
        fileId=res['files'][0]['id'],
        media_body=media_body
    ).execute()

参考資料