7

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.

joejoejoejoe4
  • 1,206
  • 1
  • 18
  • 38
  • Did my answer show you the result what you want? Would you please tell me about it? That is also useful for me to study. If this works, other people who have the same issue with you can also base your question as a question which can be solved. If you have issues for my answer yet, I apologize. At that time, can I ask you about your current situation? I would like to study to solve your issues. – Tanaike Jan 16 '19 at 23:12
  • Is there anything that I can do for your question? If my answer was not useful for your situation. I have to apologize and modify it. If you can cooperate to resolve your issue, I'm glad. I would like to think of about the solution. – Tanaike Dec 26 '19 at 23:01

4 Answers4

6

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)
Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
1

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:

If I misunderstand your question, I'm sorry.

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Is there any way to get a link for readable document(word document) without allowing user to download it? – Tejas Gaykar Dec 26 '19 at 21:19
  • @Tejas Gaykar Thank you for your comment. Unfortunately, I cannot understand about your goal from your comment. This is due to my poor English skill. I deeply apologize for this. So can you post it as new question by including the detail information? By this, it will help users think of the issue and solution. If you can cooperate to resolve your issue, I'm glad. – Tanaike Dec 26 '19 at 23:01
  • Where and how is permissionId coming into service.permissions().get() – shakthydoss Jun 29 '20 at 15:14
  • @Tanaike Do you know why I can't set permissions for an uploaded file? https://stackoverflow.com/questions/62699404/how-to-update-permissions-in-google-drive-api-v3 – F. Vosnim Jul 02 '20 at 18:23
1

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()
Jane Kathambi
  • 695
  • 6
  • 8
0

Typo: Please note that in the initial code it says "type" in the function header of update_permission but then in the corrected snippet, the reply uses "types".