OneDrive のファイル操作（登録／取得）を自動化する

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

## 概要

- OneDriveへ自動アクセスするため`Azure アプリ`を登録します。
- OneDriveへログインし、認証コードを取得します。
- 認証コードからアクセストークンを取得します。
- アクセストークンを利用して[Microsoft Graph](https://docs.microsoft.com/ja-jp/graph/overview)により「OneDriveへファイルアップロード」「OneDriveからのファイルダウンロード」を実現します。

## OneDriveへ自動アクセスするため`Azure アプリ`を登録

- 下記手順を参考に`Azure アプリ`を登録します。  
[Microsoft にアプリを登録する](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/app-registration?view=odsp-graph-online#register-your-app-with-microsoft)  
操作は[Azure Portal](https://aka.ms/AppRegistrations/?referrer=https%3A%2F%2Fdev.onedrive.com)から実施します。  
![image](https://user-images.githubusercontent.com/38905609/182548269-a145c4ac-f5d9-460f-a34e-55894662f425.png)
![image](https://user-images.githubusercontent.com/38905609/182548779-f7262de3-c04b-4c59-b1fa-bc803429c4a7.png)
- リダイレクトURLには`http://localhost:4200/`を入力します。  
![image](https://user-images.githubusercontent.com/38905609/182549332-d524a384-fb58-4ab7-9143-a5556efe8005.png)
- 概要を選択し、クライアントIDを取得します。  
![image](https://user-images.githubusercontent.com/38905609/182562419-dc1074f7-3cd7-4b47-b7c2-a3d89b59729b.png)
- 証明書とシークレットを選択し、新しいクライアントシークレットを選択します。  
![image](https://user-images.githubusercontent.com/38905609/182563162-7410ecc1-0a0d-4058-89c9-f2ff0400b889.png)
- 任意の有効期限を指定します。  
![image](https://user-images.githubusercontent.com/38905609/182564081-21b8e319-325a-4bc6-a379-28469214dd74.png)
- クライアントシークレットを取得します。  
※ クライアントシークレットとして取得するのは「値」の方です。(シークレットIDの方ではありません。)
![image](https://user-images.githubusercontent.com/38905609/182564934-f8607e73-9726-4331-9e66-03f90c733349.png)

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

- 下記手順を参考にOneDriveへのサインインを組み込みます。  
[ユーザーのサインイン](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#token-flow)  

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

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

```text
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=xxxxxxxxxxxxxxxxxxxxxxxx&scope=offline_access%20files.readwrite.all&response_type=code&redirect_uri=http://localhost:4200/
```

- サインインが完了すると認証コードが発行されるので取得します。  
![image](https://user-images.githubusercontent.com/38905609/182812282-2d40ff2f-a0b9-4788-acdd-e5543af1539b.png)

- `code=xxxxxxxxxxxxx`と表示されます。この`xxxxxxxxxxxxx`が認証コードです。  
![image](https://user-images.githubusercontent.com/38905609/182813099-c8964598-daef-4464-8cd5-39508bf1758b.png)

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

- 下記手順を参考に認証コードからアクセストークンを取得します。  
[アクセストークンの取得](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#step-2-redeem-the-code-for-access-tokens)  

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

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

def get_onedrive_token(code: str):

    apl_client_id= "bbbbbbbbbbbbbbbbb"
    client_secret = "ccccccccccccccccccc"
    redirect_url = "http://localhost:4200/"

    url = "https://login.microsoftonline.com/common/oauth2/v2.0/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_onedrive_token(code)
```

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

```json
{
  "token_type":"bearer",
  "expires_in": 3600,
  "scope":"wl.basic onedrive.readwrite",
  "access_token":"EwCo...AA==",
  "refresh_token":"eyJh...9323"
}
```

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

- 下記手順を参考にリフレッシュトークンからアクセストークンを取得します。  
[新しいアクセストークンの取得](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#step-3-get-a-new-access-token-or-refresh-token)  

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

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

def get_onedrive_reflesh_token(refresh_token: str):

    apl_client_id= "bbbbbbbbbbbbbbbbbbbb"
    client_secret = "ccccccccccccccccccc"
    redirect_url = "http://localhost:4200/"

    url = "https://login.microsoftonline.com/common/oauth2/v2.0/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("refresh_token:" + dat["refresh_token"])
        print("access_token:" + dat["access_token"])

if __name__ == '__main__':

    refresh_token = "aaaaaaaaaaaaaaaaaaaaa"
    get_onedrive_reflesh_token(refresh_token)
```

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

```json
{
  "token_type":"bearer",
  "expires_in": 3600,
  "scope": "wl.basic onedrive.readwrite wl.offline_access",
  "access_token":"EwCo...AA==",
  "refresh_token":"eyJh...9323"
}
```

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

- 下記手順を参考にアクセストークンをHTTP ヘッダーへ設定します。  
[アクセストークンの利用](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/?view=odsp-graph-online#1-user-authentication-and-authorizing-your-app)  

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

- 下記手順を参考にOneDriveのフォルダ内容一覧を取得します。  
[driveItem の子を一覧表示する](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_list_children?view=odsp-graph-online#http-request)

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

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

def get_onedrive_list(access_token: str):

    url = "https://graph.microsoft.com/v1.0/me/drive/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()
        dat = json.loads(body)
        if dat["value"]:
            list = dat["value"]
            for item in list:
                print(item)
                print("  ")

if __name__ == '__main__':

    access_token = "aaaaaaaaaaaaaaaaaa"
    get_onedrive_list(access_token)
```

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

```json
HTTP/1.1 200 OK
Content-type: application/json

{
  "value": [
    {"name": "myfile.jpg", "size": 2048, "id": "bbbbbbbbbbbbbb", "file": {} },
    {"name": "Documents", "id": "bbbbbbbbbbbbbb", "folder": { "childCount": 4} },
    {"name": "Photos", "id": "bbbbbbbbbbbbbb", "folder": { "childCount": 203} },
    {"name": "my sheet(1).xlsx", "id": "bbbbbbbbbbbbbb", "size": 197 }
  ],
  "@odata.nextLink": "https://..."
}
```

## OneDriveへのファイル登録

- 下記手順を参考にOneDriveへファイルを登録します。  
[ファイルをアップロードする](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online#create-an-upload-session)

- `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)

            current_pos = current_pos + next_size

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

    url = "https://graph.microsoft.com/v1.0/me/drive/items/" + urllib.parse.quote(parent_id) + ":/"+ urllib.parse.quote(target_file.name) + ":/createUploadSession"
    method = "POST"
    headers = {
        'Content-Type': 'application/json'
        ,'Authorization': 'bearer ' + access_token
    }

    params = {
        "item": {
            "@odata.type": "microsoft.graph.driveItemUploadableProperties",
            "@microsoft.graph.conflictBehavior": "replace",
            "name": target_file.name
        }
      }

    encoded_param = json.dumps(params).encode("utf-8")

    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)
        upload_url = dat["uploadUrl"]
        
        upload_file_session(access_token,upload_url,target_file)
```

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

- 下記手順を参考にOneDriveからファイルをダウンロードします。
[DriveItem のコンテンツをダウンロードする](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_get_content?view=odsp-graph-online#http-request)

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

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

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

    url = "https://graph.microsoft.com/v1.0/me/drive/items/" + urllib.parse.quote(item_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()
        dat = json.loads(body)
        item_name = dat["name"]
        
        download_url = dat["@microsoft.graph.downloadUrl"]
        print("download_url:" + download_url)
        
        save_name = "c:/tmp/" + item_name
        urllib.request.urlretrieve(download_url, save_name)        
        

if __name__ == '__main__':

    access_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaa"
    item_id = "bbbbbbbbbbbbbbbbbbbbbbb"

    download_file(access_token,item_id)
```

## 👇関連記事

- [PythonでQRコードを作成する](https://docs.saurus12.com/python/qrcode)

## 👇参考URL

- [Microsoft にアプリを登録する](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/app-registration?view=odsp-graph-online#register-your-app-with-microsoft)
- [Azure アプリ登録](https://aka.ms/AppRegistrations/?referrer=https%3A%2F%2Fdev.onedrive.com)
- [認証](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#code-flow)
- [新しいアクセス トークンまたは更新トークンを取得する](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/graph-oauth?view=odsp-graph-online#step-3-get-a-new-access-token-or-refresh-token)
- [アクセストークンの利用](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/getting-started/?view=odsp-graph-online#1-user-authentication-and-authorizing-your-app)
- [エンドポイント](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/concepts/direct-endpoint-differences?view=odsp-graph-online#onedrive-personal-accounts)
- [driveItem の子を一覧表示する](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_list_children?view=odsp-graph-online#http-request)
- [DriveItem のコンテンツをダウンロードする](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_get_content?view=odsp-graph-online#http-request)
- [ファイルをアップロードする](https://docs.microsoft.com/ja-jp/onedrive/developer/rest-api/api/driveitem_createuploadsession?view=odsp-graph-online#create-an-upload-session)

## 本記事へのリンク

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708851/8678e7ec-5e60-0cd3-acff-03c99ee47037.png)

### [https://docs.saurus12.com/python/onedrive](https://docs.saurus12.com/python/onedrive)

[keywords]  
OneDrive Python  

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

更新日:2024年01月14日