【问题标题】:Google Drive API with Python not allowing file download despite correct scopes being added尽管添加了正确的范围,但带有 Python 的 Google Drive API 仍不允许文件下载
【发布时间】:2020-03-31 09:44:27
【问题描述】:

我已遵循 Google 为使用 Python 提供的 quickstart,并且我已使用 Google 提供的适当范围从 Drive https://www.googleapis.com/auth/drive.readonly 下载文件,但我不断收到以下错误:

googleapiclient.errors.HttpError:https://www.googleapis.com/drive/v3/files/1RWpLGCWldcJyVqa0tIVlScg60ExEtcNIvJ7R9M8DuhM?alt=media 返回“只能下载具有二进制内容的文件。使用导出 使用 Google Docs 文件。”

当我尝试运行代码来下载文件时。

我可以读取驱动器上的文件,但我似乎无法从驱动器下载特定的电子表格,尽管我已尽了最大努力。下面是我的代码(经过编辑的文件路径和一些注释),用于通过 API 建立连接:

def gsuite_connect():

    file_path = 'OMITTED/Loading'

    # Get what permissions the user (using the API) will need. This as been set to high level
    # access by default
    scopes = ['https://www.googleapis.com/auth/drive.readonly']
    
    # Access the tokens for G Suite to access the Drive. Ensure that if this file previous exists,
    # that it is in the current working directory
    store = file.Storage(os.path.join(file_path, 'storage.json'))
    
    # Access the credentials for the Drive API
    creds = store.get()
    
    if not creds or creds.invalid:
        print("\nUsing credentials found in client_id(secret).json")
        flow = client.flow_from_clientsecrets(os.path.join(file_path, 'client_id.json'), scopes)
        creds = tools.run_flow(flow, store)
    
    http = creds.authorize(Http())
    drive = discovery.build('drive', 'v3', http=http)
    sheets = discovery.build('sheets', 'v4', http=http)
    
    return drive, sheets

这是我用于根据Google provides 下载文件的功能(编辑的文件路径和一些评论):

    def get_datalog(self):

        dir_path = 'OMITTED/Downloads'
        fname = "'FILENAME'"
        files = self.drive.files().list(q="name = {}".format(fname),
                                        fields="nextPageToken, files(id, name)").execute()
        items = files.get('files', [])

        # Error checking and subsequent downloading if file successfully found
        if not items:
            exit()
        else:

            # Change into the desired directory for storing the file and download file based on the
            # retrieved ID
            os.chdir(dir_path)
            file_id = items[0]['id']

            # Request download service
            request = self.drive.files().get_media(fileId=file_id)

            fh = io.FileIO(fname, mode='w')
            downloader = MediaIoBaseDownload(fh, request)
            done = False
            while done is False:
                status, done = downloader.next_chunk()
                print("Download %d%%." % int(status.progress() * 100))

            # Return the file path
            return os.path.join(dir_path, fname)

我们将不胜感激!我不想显示敏感文件,例如 client_id.json 或任何其他凭据,但如果您需要更多信息,请告诉我!

【问题讨论】:

  • 代码本身没有任何错误,应该可以正常工作,这让我知道您要下载什么类型的文件。我认为这与文件格式和大小有关,因此请指定此详细信息以便正确调试。
  • 使用:developers.google.com/drive/api/v3/reference/files/get 查看文件的 MIME 类型。
  • 非常感谢 Santhosh 的快速回复。这是一个谷歌表。我也无法访问其他文件,也无法从工作表中读取数据。我遇到了同样的错误,这让我认为可能还有其他事情在起作用。
  • 我遇到了同样的错误。在我的情况下,原因是我的文件 ID 错误。我在谷歌驱动器中打开文件并从浏览器地址栏中的 url 获取 ID。这是错误的。所以我所做的是通过代码检索所有文件,使用 API,我注意到文件 ID 不同。所以当我使用从 API 返回的文件 id 时,我能够使用这个 id 来检索文件。我怀疑这对任何人都有帮助,但这是我的两分钱。

标签: python google-api google-drive-api


【解决方案1】:
  • 您想使用带有 python 的 google-api-python-client 下载 Google 文档(在您的情况下,它是电子表格。)。
  • 你想知道Only files with binary content can be downloaded. Use Export with Google Docs files.出错的原因
  • 您已经能够使用 Drive API。

如果我的理解是正确的,那么这个答案呢?

修改点:

  • 当通过get_media方法下载Google Docs文件时,会出现这样的错误。
    • get_media方法的情况下,可以下载除Google Docs(电子表格、文档、幻灯片等)以外的文件。
  • 当您想下载 Google Docs 文件时,请使用export_media 方法。
    • 在这种情况下,由于谷歌方面的规范,无法下载原始谷歌文档。所以请把它转换成其他格式。例如电子表格的情况下,它是 Excel 格式、CSV 格式等。

修改脚本:

为了避免这个问题,下面的修改如何?

从:
request = self.drive.files().get_media(fileId=file_id)
到:
request = self.drive.files().export_media(fileId=file_id, mimeType='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
  • 在这种情况下,Google 电子表格将作为 Excel 文件下载。如果您想以 CSV 格式下载,请将 mimeType 修改为 text/csv

注意:

  • 在这种情况下,它假定您要下载的 Google 文档文件是您的或公开共享的。

参考资料:

如果我误解了您的问题并且这不是您想要的方向,我深表歉意。

补充:

关于更改访问令牌范围的方法,请重命名或删除脚本中storage.json的文件,然后重新运行脚本。这样,您可以重新授权新范围和创建包括令牌的新文件。您可以将访问令牌与新范围一起使用。

【讨论】:

  • 非常感谢您的详细回答@Tanaike。不幸的是,尽管我使用了https://www.googleapis.com/auth/drive.readonly 的范围,但我仍然收到以下错误:"Insufficient Permission: Request had insufficient authentication scopes.">。然而!我刚刚注意到包含我的scopescopes 字段的storage.json 文件不断将范围更改回https://www.googleapis.com/auth/drive.metadata.readonly,这是我在从Google 运行通用quickstart.py 时第一次使用的范围。是否有我需要删除的隐藏文件?
  • @user2013373 感谢您的回复。我带来的不便表示歉意。在您的情况下,请重命名或删除storage.json 的文件并再次运行脚本。这样,您可以重新授权新范围并创建新文件。您可以将访问令牌与新范围一起使用。如果我误解了您的回复,我深表歉意。
  • 嗨@Tanaike,绝对不需要道歉!你非常有帮助,我能够通过这样做来解决这个问题,我只是来这里告诉你,但看到你也找到了解决方案!再次感谢您抽出宝贵时间提供帮助:)
  • @user2013373 感谢您的回复。如果上述方法解决了您的问题,我很高兴。
  • @user2013373 我能问一下您当前的问题吗?如果我的回答不能完全解决您的问题,请告诉我。我想修改它。
【解决方案2】:

我正在使用它,它适用于以下库:

google-auth-oauthlib==0.4.1
google-api-python-client
google-auth-httplib2

这是我正在使用的 sn-p:

from apiclient import errors
from googleapiclient.http import MediaIoBaseDownload
from googleapiclient.discovery import build

def download_google_document_from_drive(self, file_id):
    try:

        request = self.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
    except Exception as e:
        print('Error downloading file from Google Drive: %s' % e)

您可以将文件流写入文件:

import xlrd
workbook = xlrd.open_workbook(file_contents=fh.getvalue())

至于我使用的范围如下 sn-ps:

def __init__(self):
  self.service = build('drive', 'v3',
                    credentials=self._service_account_credentials())

def _service_account_credentials(self.):
   service_account_key_path = os.getenv('GOOGLE_APPLICATION_CREDENTIALS')

   credentials = service_account.Credentials.from_service_account_file(
     service_account_key_path)
   scoped_credentials = credentials.with_scopes(
     ['https://www.googleapis.com/oauth2/v4/token'])
   signer_email = scoped_credentials.service_account_email
   signer = scoped_credentials.signer

   credentials = google.oauth2.service_account.Credentials(
     signer,
     signer_email,
     token_uri='https://www.googleapis.com/oauth2/v4/token'
   )
   return credentials

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-08
    • 1970-01-01
    • 1970-01-01
    • 2023-02-03
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2019-08-27
    相关资源
    最近更新 更多