Search code examples
pythonpython-3.xpermissionsgoogle-drive-api

Google Drive API v3 Change File Permissions and Get Publicly Shareable Link (Python)


I'm trying to use the Google Drive API v3 with Python 3 to automatically upload files, make them 'public' and get a shareable link that anybody, whether logged into a Google Account or not, can view and download (but not modify).

I'm close, but can't quite figure it out! Observe my code. It requires a text file named "testing.txt" to be in the same directory as the script:

from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools

from apiclient.http import MediaFileUpload
from apiclient import errors

# https://developers.google.com/drive/api/v2/about-auth#requesting_full_drive_scope_during_app_development
SCOPES = 'https://www.googleapis.com/auth/drive' # https://stackoverflow.com/a/32309750

# https://developers.google.com/drive/api/v2/reference/permissions/update
def update_permission(service, file_id, permission_id, new_role, type):
  """Update a permission's role.

  Args:
    service: Drive API service instance.
    file_id: ID of the file to update permission for.
    permission_id: ID of the permission to update.
    new_role: The value 'owner', 'writer' or 'reader'.

  Returns:
    The updated permission if successful, None otherwise.
  """
  try:
    # First retrieve the permission from the API.
    permission = service.permissions().get(fileId=file_id, permissionId=permission_id).execute()
    permission['role'] = new_role
    permission['type'] = type
    return service.permissions().update(fileId=file_id, permissionId=permission_id, body=permission).execute()
  except errors.HttpError as error:
    print('An error occurred:', error)
  return None

if __name__ == '__main__':
    # credential things
    store = file.Storage('token.json')
    creds = store.get()
    if not creds or creds.invalid:
        flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
        creds = tools.run_flow(flow, store)
    drive_service = build('drive', 'v3', http=creds.authorize(Http()))

    # create and upload file
    file_metadata = {'name': 'testing.txt'}
    media = MediaFileUpload('testing.txt',
                            mimetype='text/txt')
    file = drive_service.files().create(body=file_metadata,
                                        media_body=media,
                                        fields='id, webViewLink, permissions').execute()

    # get information needed to update permissions
    file_id = file['id']
    permission_id = file['permissions'][0]['id']

    print(file_id)
    print(permission_id)

    # update permissions?  It doesn't work!
    update_permission(drive_service, file_id, permission_id, 'reader', 'anyone') # https://stackoverflow.com/a/11669565

    print(file.get('webViewLink'))

When I run this code, I receive the following:

1quyzYHc0uCQIEt88gqd4h_jWtlBaoHHH
01486072639937946874
An error occurred: <HttpError 403 when requesting https://www.googleapis.com/drive/v3/files/1quyzYHc0uCQIEt88gqd4h_jWtlBaoHHH/permissions/01486072639937946874?alt=json returned "The resource body includes fields which are not directly writable.">
https://drive.google.com/file/d/1quyzYHc0uCQIEt88gqd4h_jWtlBaoHHH/view?usp=drivesdk

When I copy and paste the final link into another browser, it is not available, so clearly it did not succeed in changing the file permissions. But I don't understand why it failed. It mentions that The resource body includes fields which are not directly writable, but I don't know what this means.

Can somebody please help me understand what I'm doing wrong and what I need to change to fix it? Thanks.


Solution

  • I think that you have already been able to upload the file. So I would like to propose about the modification of the function of update_permission().

    Modification point:

    • I think that in your situation, it is required to add the permission by creating.
      • So you can use service.permissions().create().
      • When you want to update the created permission, please use the id retrieved by the creation of permission.

    Modified script:

    Please modify update_permission() as follows.

    From:

    try:
      # First retrieve the permission from the API.
      permission = service.permissions().get(fileId=file_id, permissionId=permission_id).execute()
      permission['role'] = new_role
      permission['type'] = type
      return service.permissions().update(fileId=file_id, permissionId=permission_id, body=permission).execute()
    except errors.HttpError as error:
      print('An error occurred:', error)
    return None
    

    To:

    try:
      permission = {
          "role": new_role,
          "type": types,
      }
      return service.permissions().create(fileId=file_id, body=permission).execute()
    except errors.HttpError as error:
      print('An error occurred:', error)
    return None
    

    Note:

    • This modified script supposes that your environment can use Drive API.

    Reference: