GoogleDrive のファイル操作（アップロード／ダウンロード）を自動化する

Pythonを使って「GoogleDriveへファイルアップロード」「GoogleDriveからのファイルダウンロード」を実現する手順を解説します。

## 概要

- GoogleDriveへ自動アクセスするため`Google Cloud project`を登録します。
- GoogleDriveへログインし、認証コードを取得します。
- 認証コードからアクセストークンを取得します。
- アクセストークンを利用して[Google Drive API](https://developers.google.com/drive/api)により「GoogleDriveへのファイルアップロード」「GoogleDriveからのファイルダウンロード」を実現します。

## GoogleDriveへ自動アクセスするため`Google Cloud project`を登録

- 下記手順を参考に`Google Cloud project`を登録します。  
[Create a Google Cloud project](https://developers.google.com/workspace/guides/create-project)  
操作は[Google Cloud console](https://console.cloud.google.com/)から実施します。  
![image](https://user-images.githubusercontent.com/38905609/183871399-32ada72b-a1e2-496c-9133-81c567f8e268.png)
![image](https://user-images.githubusercontent.com/38905609/183874180-c65fd80a-0b89-4302-a785-0ec4c115b204.png)

- ライブラリを選択します。  
![image](https://user-images.githubusercontent.com/38905609/183875632-02e477b2-5302-47f3-8e70-ab3a9052692e.png)

- Google Drive を検索し有効化します。  
![image](https://user-images.githubusercontent.com/38905609/183876150-1b451006-e2b4-419e-9644-cf1abe78adb5.png)
![image](https://user-images.githubusercontent.com/38905609/183876482-133c2387-dd67-437e-86bb-6b1247ad0cdc.png)
![image](https://user-images.githubusercontent.com/38905609/183876735-2e3146e8-c0e8-4c8d-a0b1-2996efd03980.png)
![image](https://user-images.githubusercontent.com/38905609/183877039-1c7d4b4a-f3c0-42c4-80b9-68b7aa0a6a03.png)

## OAuthの設定

- 下記手順を参考に`OAuth`を設定します。
[Configure the OAuth consent screen](https://developers.google.com/workspace/guides/configure-oauth-consent)  
操作は[Google Cloud console](https://console.cloud.google.com/)から実施します。  
- OAuth 同意画面を選択します。  
![image](https://user-images.githubusercontent.com/38905609/183878105-ecd57d53-a3b3-4e95-8c23-7118ce3cc2c2.png)
- OAuth を設定します。  
![image](https://user-images.githubusercontent.com/38905609/183878719-b6236944-9342-4022-bf30-66adee60c553.png)
![image](https://user-images.githubusercontent.com/38905609/183879291-1b33649e-7dc6-4574-80e3-d0325e324065.png)
![image](https://user-images.githubusercontent.com/38905609/183879643-91264ab6-6d67-41ba-ab13-c8682bcb6262.png)
![image](https://user-images.githubusercontent.com/38905609/183879979-0b62cc7b-130d-41e6-9cf9-d40629bef936.png)

- `../auth/drive`を選択します  
![image](https://user-images.githubusercontent.com/38905609/183882391-df82411c-432c-4aaf-9d48-715edf557511.png)
![image](https://user-images.githubusercontent.com/38905609/183883082-32ed421f-be67-464f-a2da-53e6c06d098d.png)
![image](https://user-images.githubusercontent.com/38905609/183883398-96f7a0ed-f28a-44e0-ad94-278dbc242849.png)
![image](https://user-images.githubusercontent.com/38905609/183883602-3c4240f3-2310-452b-ad55-a2d66c964343.png)

- アプリを公開します。  
![image](https://user-images.githubusercontent.com/38905609/184040131-5846785a-998a-4be9-9634-e1b97a2475d1.png)
![image](https://user-images.githubusercontent.com/38905609/184040476-0753d645-a9c0-46a8-81fe-fb1f7cdc160b.png)
![image](https://user-images.githubusercontent.com/38905609/184040708-2df2b499-e4f9-4d0f-a875-4bad3a8fa5c9.png)

- 下記手順を参考に認証情報を作成します。
[OAuth client ID credentials](https://developers.google.com/workspace/guides/create-credentials#oauth-client-id)  

![image](https://user-images.githubusercontent.com/38905609/184041378-19a18915-0c3f-46a5-a783-187e54199f5d.png)
![image](https://user-images.githubusercontent.com/38905609/184041518-67e07474-2f0f-41be-8f72-25e900339adb.png)
![image](https://user-images.githubusercontent.com/38905609/184041712-b5533bfe-a66e-45ff-85f4-d057cdb82808.png)

- 承認済みのリダイレクト URIに`http://localhost:4200/`を設定します。  
![image](https://user-images.githubusercontent.com/38905609/184041920-6844f9bf-ccb4-4ff5-a5f7-e73c360c83b8.png)

- **クライアントID** と **クライアントシークレット** を取得します。  
![image](https://user-images.githubusercontent.com/38905609/184042209-16e20b73-cd7b-464c-be5d-dcf66afbcd6c.png)

## GoogleDriveへログインし、認証コードを取得

- 下記手順を参考にGoogleDriveへのサインインを組み込みます。  
[Google の OAuth 2.0 サーバーにリダイレクトする](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#redirecting)  

トークン取得用URLを作成し、ブラウザへ貼り付けます。  

- 下記の`client_id=xxxxxxxxxxxxxxxxxxxxxxxx`には上記で取得したクライアントIDを指定します。

```text
https://accounts.google.com/o/oauth2/v2/auth?scope=https%3A//www.googleapis.com/auth/drive&access_type=offline&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2F&client_id=xxxxxxxxxxxxxxxxxxxxxxxx
```

- サインインが完了すると認証コードが発行されるので取得します。  
![image](https://user-images.githubusercontent.com/38905609/184043735-1239c0e9-90b3-4ad2-b4e1-5e5c7613a20e.png)
![image](https://user-images.githubusercontent.com/38905609/184043923-30d864cc-4721-4405-83d7-836664d823e5.png)
![image](https://user-images.githubusercontent.com/38905609/184044070-31452550-783f-4924-ab9d-680a852a48ce.png)

- `code=xxxxxxxxxxxxx`と表示されます。この`xxxxxxxxxxxxx`が認証コードです。  
![image](https://user-images.githubusercontent.com/38905609/184044277-9c58eb8d-ccba-4249-8987-70669f79cab0.png)
**注意**  
URLに表示される認証コードはURLエンコードされています。  
認証コードはデコードしてから利用する必要があります。  
例 4%2F0Ad・・・ => 4/0Ad・・・  
[参考サイト](https://tech-unlimited.com/urlencode.html)  

## 認証コードからアクセストークンを取得

- 下記手順を参考に認証コードからアクセストークンを取得します。  
[更新トークンとアクセス トークンの認証コードを交換する](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#exchange-authorization-code)  

- `aaaaaaaaaaaaaaaaa`には上記で取得した認証コードを指定します。
- `bbbbbbbbbbbbbbbbb`には上記で取得したクライアントIDを指定します。
- `ccccccccccccccccc`には上記で取得したクライアントシークレットを指定します。

```python
import urllib.parse
import urllib.request
import json

def get_googledrive_token(code: str):

    apl_client_id= "bbbbbbbbbbbbbbbbbbbbbbbbb"
    client_secret = "cccccccccccccccccccccccc"
    redirect_url = "http://localhost:4200/"

    url = "https://oauth2.googleapis.com/token"
    method = "POST"
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    params = {
        "client_id": apl_client_id
        ,"redirect_uri": redirect_url
        ,"client_secret": client_secret
        ,"code": code
        ,"grant_type": "authorization_code"
    }

    encoded_param = urllib.parse.urlencode(params).encode()

    request = urllib.request.Request(url, data=encoded_param, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
        dat = json.loads(body)
        print(dat)

if __name__ == '__main__':

    code = "aaaaaaaaaaaaaaaaaaaaaaaa"
    get_googledrive_token(code)
```

- 下記のようなjsonが取得できます。  
- この中に以降の処理で必要なアクセストークン(`access_token`)及びリフレッシュトークン(`refresh_token`)が含まれます。

```json
{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "token_type": "Bearer",
  "scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
  "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
```

## アクセストークンの更新

- 下記手順を参考にリフレッシュトークンからアクセストークンを取得します。  
[アクセス トークンの更新（オフライン アクセス）](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#offline)  

- `aaaaaaaaaaaaaaaaa`には上記で取得したリフレッシュトークンを指定します。
- `bbbbbbbbbbbbbbbbb`には上記で取得したクライアントIDを指定します。
- `ccccccccccccccccc`には上記で取得したクライアントシークレットを指定します。

```python
import urllib.parse
import urllib.request
import json

def get_googledrive_reflesh_token(refresh_token: str):

    apl_client_id= "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
    client_secret = "cccccccccccccccccccccccccccccccccc"
    redirect_url = "http://localhost:4200/"

    url = "https://oauth2.googleapis.com/token"
    method = "POST"
    headers = {
        'Content-Type': 'application/x-www-form-urlencoded'
    }

    params = {
        "client_id": apl_client_id
        ,"redirect_uri": redirect_url
        ,"client_secret": client_secret
        ,"refresh_token": refresh_token
        ,"grant_type": "refresh_token"
    }

    encoded_param = urllib.parse.urlencode(params).encode()

    request = urllib.request.Request(url, data=encoded_param, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
        dat = json.loads(body)
        print(dat)
        print("access_token:" + dat["access_token"])

if __name__ == '__main__':

    refresh_token = "aaaaaaaaaaaaaaaaaaaaaa"
    get_googledrive_reflesh_token(refresh_token)

```

- 下記のようなjsonが取得できます。  
- この中に以降の処理で必要なアクセストークン(`access_token`)が含まれます。

```json
{
  "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
  "expires_in": 3920,
  "scope": "https://www.googleapis.com/auth/drive.metadata.readonly",
  "token_type": "Bearer"
}
```

## アクセストークンの利用

- 下記手順を参考にアクセストークンをHTTP ヘッダーへ設定します。  
[Google API の呼び出し](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#callinganapi)  

## GoogleDriveのフォルダ内容一覧取得

- 下記手順を参考にGoogleDriveのフォルダ内容一覧を取得します。  
[https://developers.google.com/drive/api/v2/reference/children/list](https://developers.google.com/drive/api/v2/reference/children/list)  
[https://developers.google.com/drive/api/v3/reference/files/get](https://developers.google.com/drive/api/v3/reference/files/get)  

- `aaaaaaaaaaaaaaaaa`には上記で取得したアクセストークンを指定します。

```python
import urllib.parse
import urllib.request
import json

def get_googledrive_item(access_token: str, id: str):

    url = "https://www.googleapis.com/drive/v3/files/" + id
    method = "GET"
    headers = {
        'Authorization': 'Bearer ' + access_token
    }

    request = urllib.request.Request(url, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
        print(body)

def get_googledrive_list(access_token: str):

    url = "https://www.googleapis.com/drive/v2/files/root/children"
    method = "GET"
    headers = {
        'Authorization': 'Bearer ' + access_token
    }

    request = urllib.request.Request(url, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
        #print(body)
        dat = json.loads(body)
        print(dat)
        if dat["items"]:
            list = dat["items"]
            for item in list:
                print(item["id"])
                get_googledrive_item(access_token,item["id"])
                print("  ")

if __name__ == '__main__':

    access_token = "aaaaaaaaaaaaaaaaaaa"
    get_googledrive_list(access_token)
```

- 下記のようなjsonが取得できます。  

```json
{'kind': 'drive#file', 'id': 'xxxxxxxxxxxxx', 'name': 'pictures', 'mimeType': 'application/vnd.google-apps.folder'}
```

## GoogleDriveへのファイル登録

- 下記手順を参考にGoogleDriveへファイルを登録します。  
[Perform a resumable upload](https://developers.google.com/drive/api/guides/manage-uploads#resumable)

- `aaaaaaaaaaaaaaaaa`には上記で取得したアクセストークンを指定します。
- `bbbbbbbbbbbbbbbbb`には上記で取得した格納対象フォルダの`id`を指定します。

```python
import pathlib
import urllib.parse
import urllib.request
import json
import os

def upload_file_session(access_token: str, upload_url:str, target_file:pathlib.Path):

    contentLength = os.path.getsize(target_file)
    print("contentLength:" + str(contentLength))

    with open(target_file, 'rb') as f:

        file_split_size = 1024 * 1024 * 10

        current_pos = 0

        while current_pos < contentLength:

            remain = contentLength - current_pos
            next_size = file_split_size
            if remain <= file_split_size:
                next_size = remain

            file_body = f.read(next_size)

            reange_string = "bytes " + str(current_pos) + "-" + str(current_pos + next_size -1) + "/" + str(contentLength)
            print(reange_string)

            method = "PUT"
            headers = {
                'Content-Type': 'application/octet-stream'
                ,'Authorization': "Bearer " + access_token
                ,'Content-Length': next_size
                ,'Content-Range': reange_string
            }

            request = urllib.request.Request(upload_url, data=file_body, method=method, headers=headers)
            with urllib.request.urlopen(request) as res:
                body = res.read()
                print(body)
            except urllib.error.HTTPError as err:
                print(err)

            current_pos = current_pos + next_size



def upload_file(access_token: str, parent_id: str, target_file:pathlib.Path):

    url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable"
    method = "POST"
    headers = {
        'Content-Type': 'application/json'
        ,'Authorization': 'Bearer ' + access_token
    }

    params = {
        "name": target_file.name,
        "parents": [parent_id]
      }

    encoded_param = json.dumps(params).encode("utf-8")
    print(encoded_param)
    request = urllib.request.Request(url, data=encoded_param, method=method, headers=headers)

    with urllib.request.urlopen(request) as res:
        headers = res.info()
        print(headers)
        upload_url = headers["Location"]
        print("upload_url:" + upload_url)
        upload_file_session(access_token,upload_url,target_file)

if __name__ == '__main__':

    access_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    parent_id = "bbbbbbbbbbbbbbbbbbbbbbbbb"
    target_file = pathlib.Path("c:/tmp/テスト.docx")

    upload_file(access_token,parent_id,target_file)

```

## GoogleDriveからのファイルダウンロード

- 下記手順を参考にGoogleDriveからファイルをダウンロードします。  
[https://developers.google.com/drive/api/v3/reference/files/get](https://developers.google.com/drive/api/v3/reference/files/get)

- `aaaaaaaaaaaaaaaaa`には上記で取得したアクセストークンを指定します。
- `bbbbbbbbbbbbbbbbb`には上記で取得したダウンロード対象ファイルの`id`を指定します。

```python
import urllib.parse
import urllib.request
import urllib
import json

def download_file_content(access_token: str, item_id:str, file_name:str):

    url = "https://www.googleapis.com/drive/v3/files/" + urllib.parse.quote(item_id)

    method = "GET"
    headers = {
        'Authorization': 'Bearer ' + access_token
    }

    params = {
        "alt":"media"
      }

    param_str = urllib.parse.urlencode(params)
    print(param_str)
    url = url + "?" + param_str

    request = urllib.request.Request(url, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
        save_name = "c:/tmp/" + file_name
        with open(save_name, mode="wb") as f:
          f.write(body)


def download_file(access_token: str, item_id:str):

    url = "https://www.googleapis.com/drive/v3/files/" + urllib.parse.quote(item_id)

    method = "GET"
    headers = {
        'Authorization': 'Bearer ' + access_token
    }

    params = {
        "fields": "id,name,webContentLink",
      }

    param_str = urllib.parse.urlencode(params)
    print(param_str)
    url = url + "?" + param_str

    request = urllib.request.Request(url, method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()

        dat = json.loads(body)
        print(dat)

        file_name = dat["name"]

        print("file_name:"+ file_name)

        download_file_content(access_token,item_id,file_name)

if __name__ == '__main__':

    access_token = "aaaaaaaaaaaaaaaaaaaaaaa"
    item_id = "bbbbbbbbbbbbbbbbbbbbbbbbbbbb"

    download_file(access_token,item_id)

```

## 👇関連記事

- [OneDrive のファイル操作（登録／取得）を自動化する](https://docs.saurus12.com/python/onedrive)

## 👇参考URL

- [Create a Google Cloud project](https://developers.google.com/workspace/guides/create-project)  
- [Google Cloud console](https://console.cloud.google.com/)
- [Configure the OAuth consent screen](https://developers.google.com/workspace/guides/configure-oauth-consent)  
- [OAuth client ID credentials](https://developers.google.com/workspace/guides/create-credentials#oauth-client-id)  
- [Google の OAuth 2.0 サーバーにリダイレクトする](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#redirecting)  
- [更新トークンとアクセス トークンの認証コードを交換する](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#exchange-authorization-code)  
- [アクセス トークンの更新（オフライン アクセス）](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#offline)  
- [Google API の呼び出し](https://developers.google.com/identity/protocols/oauth2/web-server?authuser=0#callinganapi)  
- [Perform a resumable upload](https://developers.google.com/drive/api/guides/manage-uploads#resumable)
- [https://developers.google.com/drive/api/v3/reference/files/get](https://developers.google.com/drive/api/v3/reference/files/get)

[keywords]  
GoogleDrive TypeScript Python  

[GoogleDrive のファイル操作（アップロード／ダウンロード）を自動化する](https://docs.saurus12.com/python/google_drive)

更新日:2024年02月07日