Search code examples
pythongoogle-apigoogle-drive-apigoogle-api-python-clientservice-accounts

Google Drive Authenticate and Download Files with Service Account


I've been trying to use a service account to download files from Google Drive via the API and some examples I found in the documentation, and for a while it worked fine. A few days ago though, I started getting this error:

{
 "error": {
  "errors": [
   {
    "domain": "usageLimits",
    "reason": "dailyLimitExceededUnreg",
    "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
    "extendedHelp": "https://code.google.com/apis/console"
   }
  ],
  "code": 403,
  "message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
 }
}

Well, actually I get a different 403 in my console (only files with binary content can be downloaded), but when I click on the link in that error, it leads to the above JSON.

EDIT: The above bolded part was the real issue, and then when I clicked the link it was unauthorized. My mistake!

This is the code I'm running, basically pulled from their docs:

def get_drive_service(key_file):
    sa_creds = service_account.Credentials.from_service_account_file(key_file)
    scoped_creds = sa_creds.with_scopes(SCOPES)
    drive_service = build('drive', 'v3', credentials=scoped_creds)
    return drive_service

def download_report(drive_service, id):
    request = drive_service.files().get_media(fileId=id)
    print(request.to_json())
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request, chunksize=1024*1024)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print("Download %d%%." % int(status.progress() * 100))
    return fh

drive_service = get_drive_service(<key_file>)
buffer = download_report(drive_service, <file_id>)

I found some similar questions on SO, and tried everything that seemed relevant, but nothing has worked. The API is enabled, and the quotas are not an issue, when I use files().list().execute() I get exactly the list of files I would expect, and as I said the code used to work fine.

I'm flummoxed. Can anyone please help me understand what I'm missing here?


Solution

  • Daily Limit for Unauthenticated Use Exceeded.

    This error message is normally the result of not applying the authorization credentials in your code. I couldn't spot any issues off the bat with your code though. The first thing i would like to suggest is that you double check that you have service account credentials added in your project and not the wrong type. However i would have expected a different error message if this was the issue.

    Try this its based upon the offical samples manage downloads

    from apiclient.discovery import build
    from oauth2client.service_account import ServiceAccountCredentials
    
    
    SCOPES = ['https://www.googleapis.com/auth/drive']
    KEY_FILE_LOCATION = '<REPLACE_WITH_JSON_FILE>'
    
    
    def initialize_drive():
      """Initializes an drive service object.
    
      Returns:
        An authorized drive service object.
      """
      credentials = ServiceAccountCredentials.from_json_keyfile_name(
          KEY_FILE_LOCATION, SCOPES)
    
      # Build the service object.
      service = build('drive', 'v3', credentials=credentials)
    
      return service
    
    def download_report(drive_service, id):
      file_id = '0BwwA4oUTeiV1UVNwOHItT0xfa2M'
      request = drive_service.files().get_media(fileId=file_id)
      fh = io.BytesIO()
      downloader = MediaIoBaseDownload(fh, request)
      done = False
    while done is False:
      status, done = downloader.next_chunk()
      print "Download %d%%." % int(status.progress() * 100)
     return fh
    

    only files with binary content can be downloaded

    Remember there are two types of files on drive. Google drive mimetype files which need to be downloaded using the export method and all other binary type files. Binary files are downloaded using the method you are using now.

    def main():
     service = initialize_drive()
    
     buffer = download_report(service, <file_id>)
    
    if __name__ == '__main__':
      main()
    

    Export method

    file_id = '1ZdR3L3qP4Bkq8noWLJHSr_iBau0DNT4Kli4SxNc2YEo'
    request = drive_service.files().export_media(fileId=file_id,
                                                 mimeType='application/pdf')
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print "Download %d%%." % int(status.progress() * 100)