Search code examples
pythongoogle-apigoogle-drive-apigoogle-oauthgoogle-api-python-client

google service account credentials not working


I am trying to create a simple web application that automatically uploads my database & media backup to a designated google drive. I have followed the official document and created a service account credential, gave it the owner role, and extracted a key(json file) from Google cloud platform. I enabled the Google Drive API on my account and wrote this code, but the credentials.valid returns False and my file would not upload to my drive.

from google.oauth2 import service_account
import googleapiclient as google
from googleapiclient.http import MediaFileUpload, HttpRequest
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/drive']

credentials = service_account.Credentials.from_service_account_file('./service-credentials.json', scopes=SCOPES)

print(credentials.valid)

service = build('drive', 'v3', credentials=credentials)
file_metadata = {'name' : 'python.png'}
media = MediaFileUpload('./python.png', mimetype='image/png')
file_up = service.files().create(body=file_metadata, media_body=media, fields='id').execute()

file_back = service.files().get(fileId=file_up['id']).execute()

print(file_back.get('WebContentLink'))


Solution

  • How about this modification?

    Modification points:

    • I think that in your script, service of service = build('drive', 'v3', credentials=credentials) can be used for uploading the file.
      • In my environment, I could confirm that the file can be uploaded using your script.
      • From my file would not upload to my drive., I thought that you might misunderstand about the service account. The file uploaded with the service account is created to the Drive of the service account. This Drive is different from your Google Drive of your account. I thought that this might be the reason of my file would not upload to my drive..
      • If you want to see the file uploaded with the service account at your Google Drive, it is required to share the uploaded file with your Google account. Or, it is required to upload the file to the folder in your Google Drive shared with the service account.
    • And also, in your script, file_back.get('WebContentLink') is used. In this case, None is always returned because WebContentLink is required to be WebContentLink. And also, in Drive API v3, the default returned values don't include webContentLink. So it is required to set fields.

    When above points are reflected to your script, your script becomes as follows.

    Modified script:

    from google.oauth2 import service_account
    import googleapiclient as google
    from googleapiclient.http import MediaFileUpload, HttpRequest
    from googleapiclient.discovery import build
    
    SCOPES = ['https://www.googleapis.com/auth/drive']
    credentials = service_account.Credentials.from_service_account_file('./service-credentials.json', scopes=SCOPES)
    
    service = build('drive', 'v3', credentials=credentials)
    file_metadata = {'name': 'python.png'}
    media = MediaFileUpload('./python.png', mimetype='image/png')
    file_up = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
    
    # Create a permission. Here, your Google account is shared with the uploaded file.
    yourEmailOfGoogleAccount = '###'  # <--- Please set your Email address of Google account.
    permission = {
        'type': 'user',
        'role': 'writer',
        'emailAddress': yourEmailOfGoogleAccount,
    }
    service.permissions().create(fileId=file_up['id'], body=permission).execute()
    
    file_back = service.files().get(fileId=file_up['id'], fields='webContentLink').execute()  # or fields='*'
    print(file_back.get('webContentLink'))
    
    • When you run above script, the uploaded file can be seen at "Shared with me" in your Google Drive.

    • If you want to put the specific folder of your Google Drive, please use the following script. In this case, before you run the script, please share the folder with the email of the service account. Please be careful this.

        from google.oauth2 import service_account
        import googleapiclient as google
        from googleapiclient.http import MediaFileUpload, HttpRequest
        from googleapiclient.discovery import build
      
        SCOPES = ['https://www.googleapis.com/auth/drive']
        credentials = service_account.Credentials.from_service_account_file('./service-credentials.json', scopes=SCOPES)
      
        service = build('drive', 'v3', credentials=credentials)
        file_metadata = {'name': 'python.png', 'parents': ['###']}  # <--- Please set the folder ID shared with the service account.
        media = MediaFileUpload('./python.png', mimetype='image/png')
        file_up = service.files().create(body=file_metadata, media_body=media, fields='id').execute()
      
        file_back = service.files().get(fileId=file_up['id'], fields='webContentLink').execute()  # or fields='*'
        print(file_back.get('webContentLink'))
      

    Note:

    • In the current stage, when the owner of file uploaded with the service account is changed, an error like You can't yet change the owner of this item. (We're working on it.). So I proposed above modified script.

    References: