pyDriveを使ってフォルダ丸ごとアップロードするプログラム


はじめに

pyDriveを使うための事前準備はこちらに書いてあるのでご参考にしてください。
今回作成したのはローカルのディレクトリにあるファイルを一括転送するプログラムで、2階層のディレクトリ構造で下記のように2階層目にデータファイルがある構造を想定しています。
この構成でtestディレクトリを丸々アップロードするプログラムです。
/home/static/Data/test
|
|
+- dir1
| |
| +- Data1
| +- Data2
|
+- dir2
|
+- Data3
+- Data4



とりあえずプログラム

実際に使う場合は下記を自分の環境に合わせて設定してください。その他はコピペすれば動くはずです。
    parentid:  Google Drive上のアップロード先フォルダのIDを指定、この下にデータがアップロードされます
    path: ローカルのデータ格納フォルダ

import os
import sys
import datetime

from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive

def auth_gd():
    gauth = GoogleAuth()
    gauth.CommandLineAuth()
    drive = GoogleDrive(gauth)
    return drive

def upload2googledrive():
    drive = auth_gd()
    
    parentid = 'xxxxxxxx' #project-root directory
    path = "/home/static/Data/test" #path to local data

    # "test"フォルダをGoogle Drive上に作成
    t_folder = create_dir(parentid, os.path.basename(path), drive)

    # ローカルのディレクトリ一覧取得
    flag = ""
    dir_list=get_dirlist(path)
    for d in dir_list:
        d_folder = create_dir(t_folder['id'], os.path.basename(d), drive)
#ローカルのファイル一覧取得 file_list=get_filelist(d) for f in file_list: upload_file(d_folder['id'], f, drive)
# ディレクトリがGoogle Drive上に存在するかどうかをチェックし、
# 存在しなければ作成、すでに存在すれば既存のフォルダを返す def create_dir(pid, fname, drive=None): if drive == None: drive = auth_gd() ret = check_files(pid, fname, drive) if ret == False: folder = drive.CreateFile({'title': fname, 'mimeType': 'application/vnd.google-apps.folder'}) folder['parents']= [{'id': pid}] folder.Upload() else: folder = ret print(folder['title']+" exists") return folder
#同じ名前のファイルがGoogle Drive上に存在するかチェックし、
#存在しなければアップロード、存在すれば既存のファイルを返す def upload_file(pid, fname, drive=None): if drive == None: drive = auth_gd() ret = check_files(pid, fname, drive) if ret == False: gf = drive.CreateFile() gf['parents']= [{'id': pid}] gf.SetContentFile(fname) gf['title'] = os.path.basename(fname) gf.Upload() else: gf = ret print(gf['title']+" exists") return gf
#Google Drive上にその名前のファイル/フォルダがあるかチェック、なければFalseを、あれば既存のファイル/フォルダを返す def check_files(pid, fname, drive=None): if drive == None: drive = auth_gd() query = '"{}" in parents'.format(pid) query += ' and title = "' + os.path.basename(fname) + '"' list = drive.ListFile({'q': query}).GetList() if len(list)> 0: return list[0] return False def get_dirlist(basedir): ret = [] for path, dirnames, filenames in os.walk(basedir): for dir in sorted(dirnames): d = os.path.join(path, dir) ret.append(d) return ret def get_filelist(basedir): ret = [] for path, dirnames, filenames in os.walk(basedir): for filename in sorted(filenames): fullpath = os.path.join(path, filename) ret.append(fullpath) return ret if __name__ == '__main__': upload2googledrive()


プログラム解説

Google Driveのファイル管理について

初めに、pyDriveでGoogle Driveを取り扱ううえで厄介なのがファイルがファイル名でなくIDで管理されていることです。IDで管理されているということは、同じ名前のファイルを同じフォルダにアップロードできてしまい、別ファイルとして存在できてしまいます。そのためこのプログラムではファイル名(title)でlistを確認し、同じファイル名のファイルが存在すればそれを返すようにすることで二重生成を防いでいます。毎回フォルダ作成やファイルアップロードのたびにこのチェックが入るので、けっこうなやり取りが発生します。

ファイルのアップロード

        gf = drive.CreateFile()
        gf['parents']= [{'id': pid}]
        gf.SetContentFile(fname)
        gf['title'] = os.path.basename(fname)
        gf.Upload()
CreateFileを使用し、google driveのGoogleDriveFileオブジェクトを生成します。
生成したファイルオブジェクトに属性を設定して、最後にアップロードします。

['parents']:格納するディレクトリのIDを指定します。
Google Driveはパス名やファイル名でデータを管理しておらず、IDを使ってデータ構造を管理します。
自分のIDはアップロード後に、gf['id']で参照することができます。
フォルダを作成し、その下にファイルをアップロードする場合は、作成したフォルダの['id']を取得し、アップロードするファイルの['parents']にフォルダの['id']を設定します。

フォルダの生成

        folder = drive.CreateFile({'title': fname,
                                 'mimeType': 'application/vnd.google-apps.folder'})
        folder['parents']= [{'id': pid}]
        folder.Upload()

フォルダの生成もファイルの生成とほぼ同様です。1つだけポイントは
'mimeType': 'application/vnd.google-apps.folder'
とすることです。

Google Drive上の情報取得

    query = '"{}" in parents'.format(pid)
    query += ' and title = "' + os.path.basename(fname) + '"'

    list =  drive.ListFile({'q': query}).GetList()

ListFile()を使用してGoogle Drive上のファイルの一覧を取得することができます。
'q'を使ってQueryで条件を指定することができます。
ここでは、
  • in parentsで親となるフォルダを指定している
  • title=を使ってファイル・フォルダ名を指定している
で、条件を絞り込んでいます。queryの内容はstrで指定でき、条件を"and"でつなぐことで、複数の条件を指定することができます。andでつなぐ場合はきちんと間にスペースが入っていることを確認しましょう。

ローカルファイルの一覧取得

    ret = []
    for path, dirnames, filenames in os.walk(basedir):
        for dir in sorted(dirnames):
            d = os.path.join(path, dir)
            ret.append(d)

os.walkを使用してローカルのディレクトリ・ファイル一覧を取得します。上記ではディレクトリの一覧のみを取得していますが、ファイルに関しても同じように処理できます。