Skip to content

Instantly share code, notes, and snippets.

@nandini-menon
Last active July 30, 2020 10:26
Show Gist options
  • Save nandini-menon/f29d2dd0e27592464d559dfa346e5800 to your computer and use it in GitHub Desktop.
Save nandini-menon/f29d2dd0e27592464d559dfa346e5800 to your computer and use it in GitHub Desktop.
Upload Files to OneDrive via API

Upload Files to OneDrive via API

The OneDrive API has two types of uploads:-

  • Simple Upload: For files < 4MB
  • Resumable Upload: For files > 4MB

The code for both has been provided below

Note: The Resumable upload works for files with size upto 100MB. When I tried with files with size greater than that, the upload kept failing

Azure App Requirements

Before executing this script, you need to have an App in the Azure AD with the following delegated permissions:-

  • Sites.ReadWrite.All
  • Files.ReadWrite
  • Files.ReadWrite.All

You also have to set the Redirect URI of your app to https://login.microsoftonline.com/{azure_active_directory_id}/oauth2/token.

You also need to generate a client secret key referred to as client_secret in the program.

Note: Remember to copy and store your key somewhere safely as soon as you generate it. It will not be visible for long due to security reasons.

Other requirements

The Python requests package should be installed

Code

import requests
import json
import os


class OneDriveAPI:
    def __init__(self, app_id, client_secret, tenant_id):
        self.app_id = app_id
        self.client_secret = client_secret
        self.tenant_id = tenant_id
        self.access_token = None
        self.refresh_token = None


    def authorize(self, username, password):
        url = f'https://login.microsoftonline.com/{self.tenant_id}/oauth2/token'

        token_data = {
            'grant_type': 'password',
            'client_id': self.app_id,
            'client_secret': self.client_secret,
            'resource': 'https://graph.microsoft.com',
            'scopes': ['Sites.ReadWrite.All','Files.ReadWrite.All','onedrive.readwrite', 'offline_access'],
            'username': username,
            'password': password,
        }
        token_r = requests.post(url, data=token_data)
        self.access_token = token_r.json().get('access_token')
        self.refresh_token = token_r.json().get('refresh_token')

    def refresh_access_token(self):
        url = f'https://login.microsoftonline.com/{self.tenant_id}/oauth2/token'

        token_data = {
            'grant_type': 'refresh_token',
            'client_id': self.app_id,
            'client_secret': self.client_secret,
            'resource': 'https://graph.microsoft.com',
            'scopes': ['onedrive.readwrite', 'offline_access'],
            'refresh_token': self.refresh_token,
        }
        token_r = requests.post(url, data=token_data)
        self.access_token = token_r.json().get('access_token')
        self.refresh_token = token_r.json().get('refresh_token')


    def simple_upload(self, file_name, content):
        url = f'https://graph.microsoft.com/v1.0/me/drive/root:/{file_name}:/content'
        headers = {
            'Authorization': 'Bearer {}'.format(self.access_token),
            'Content-Type': 'text/plain'
        }
        return requests.put(url=url, headers=headers, data=content)

    def large_file_upload(self, file_name):
        url = f'https://graph.microsoft.com/v1.0/me/drive/root:/{file_name}:/createUploadSession'
        headers = {
            'Authorization': 'Bearer {}'.format(self.access_token),
            'Content-Type': 'application/json'
        }
        upload_session = requests.post(url=url, headers=headers).json()

        with open(file_name, 'rb') as f:
            total_file_size = os.path.getsize(file_name)
            chunk_size = 327680
            chunk_number = total_file_size//chunk_size
            chunk_leftover = total_file_size - chunk_size * chunk_number
            i = 0
            while True:
                chunk_data = f.read(chunk_size)
                start_index = i*chunk_size
                end_index = start_index + chunk_size
                if not chunk_data:
                    break
                if i == chunk_number:
                    end_index = start_index + chunk_leftover
                headers = {'Content-Length':'{}'.format(chunk_size),'Content-Range':'bytes {}-{}/{}'.format(start_index, end_index-1, total_file_size)}
                chunk_data_upload = requests.put(upload_session['uploadUrl'], data=chunk_data, headers=headers)
                print(chunk_data_upload)
                if chunk_data_upload.status_code == 401:
                    self.refresh_access_token()
                    chunk_data_upload = requests.put(upload_session['uploadUrl'], data=chunk_data, headers=headers)
                    print(chunk_data_upload.json())
                i = i + 1


def main():
    app_id = '{app_id}'
    client_secret = '{client_secret_key}'
    tenant_id = '{azure_active_directory_id}'
    onedrive = OneDriveAPI(app_id, client_secret, tenant_id)
    onedrive.authorize('{username}', '{password}')

    # To upload a single file
    content = 'This is the content for a sample file uploaded using OneDrive API'
    print(onedrive.simple_upload('sample.txt', content))

    # To loop over and upload all the files in a directory
    for file_name in os.listdir('Path/To/Directory'):
        with open('Path/To/Directory/' + file_name, 'r') as f:
            content = f.read()
            response = onedrive.simple_upload(file_name, content)
            if response .status_code == 401:
                onedrive.refresh_access_token()
                onedrive.simple_upload(file_name, content)

    # To upload the same content from a file 'base.txt' to 50000 files
    with open('base.txt', 'r') as f:
        content = f.read()
        for i in range(1, 50001):
            file_name = 'File' + str(i).zfill(5) + '.txt'
            response = onedrive.simple_upload(file_name, content)
            if response .status_code == 401:
                onedrive.refresh_access_token()
                onedrive.simple_upload(file_name, content)

    # To upload files > 4MB
    onedrive.large_file_upload('100MBfile.txt')

if __name__ == '__main__':
    main()

To run the program, save the above code to a file and just execute the following command from command prompt/powershell/terminal:-

$ python file_name.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment