Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 382
ereHsaWyhsipS Avatar asked Jan 12 '19 18:01

ereHsaWyhsipS


3 Answers

The selected answer was not precise (nothing about type value and role) enough so I had to read the documentation a bit more, here is a working example, you just need to give the file_id:

def set_permission(service, file_id):
    print(file_id)
    try:
        permission = {'type': 'anyone',
                      'value': 'anyone',
                      'role': 'reader'}
        return service.permissions().create(fileId=file_id,body=permission).execute()
    except errors.HttpError as error:
        return print('Error while setting permission:', error)
like image 163
Antonin GAVREL Avatar answered Sep 23 '22 14:09

Antonin GAVREL


How about this modification? 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:

  • Permissions

If I misunderstand your question, I'm sorry.

like image 26
Tanaike Avatar answered Sep 23 '22 14:09

Tanaike


I created a python function to share a file using the file id and making sure that I set sendNotificationEmail=False is what solved the problem:

def share_file(file_id, email):
    
    # Share with user
    new_permissions = {
    'type': 'group',
    'role': 'writer',
    'emailAddress': email
    }

    permission_response = drive_service.permissions().create( 
        fileId=file_id, 
        body=new_permissions, 
        sendNotificationEmail=False
   ).execute()
like image 26
Jane Kathambi Avatar answered Sep 26 '22 14:09

Jane Kathambi