使用服务帐户凭据的 GDrive 导出失败并显示 404

GDrive export using Service Account creds fails with 404

我有一个脚本可以使用 OAuth 客户端从 GDrive 文件导出文本,效果非常好 -

import googleapiclient.discovery as google

from apiclient.http import MediaIoBaseDownload

from google_auth_oauthlib.flow import InstalledAppFlow

from google.auth.transport.requests import Request

import datetime, io, os, pickle

Scopes=" ".join(['https://www.googleapis.com/auth/drive.file',
                 'https://www.googleapis.com/auth/drive.metadata',
                 'https://www.googleapis.com/auth/drive.readonly'])

TokenFile="token.pickle"

def init_creds(clientfile,
               scopes,
               tokenfile=TokenFile):            
    token=None
    if os.path.exists(tokenfile):
        with open(tokenfile, 'rb') as f:
            token=pickle.load(f)            
    if (not token or
        not token.valid or
        token.expiry < datetime.datetime.utcnow()):    
        if (token and
            token.expired and
            token.refresh_token):
            token.refresh(Request())
        else:
            flow=InstalledAppFlow.from_client_secrets_file(clientfile, scopes)
            token=flow.run_local_server(port=0)
        with open(tokenfile, 'wb') as f:
            pickle.dump(token, f)
    return token

def export_text(id,
                clientfile,
                scopes=Scopes):
    creds=init_creds(clientfile=clientfile,
                     scopes=scopes)
    service=google.build('drive', 'v3', credentials=creds)
    request=service.files().export_media(fileId=id,
                                         mimeType='text/plain')
    buf=io.BytesIO()
    downloader, done = MediaIoBaseDownload(buf, request), False
    while done is False:
        status, done = downloader.next_chunk()
        destfilename="tmp/%s.txt" % id
    return buf.getvalue().decode("utf-8")

if __name__=='__main__':
    print (export_text(id="#{redacted}"
                       clientfile="/path/to/oath/client.json"))

但是每次都必须通过 OAuth 流程是一件很痛苦的事情,因为只有我在使用脚本,所以我想简化事情并改用服务帐户,从这个 post 开始 -

我的新服务帐户脚本执行完全相同的操作,如下所示 -

import googleapiclient.discovery as google

from oauth2client.service_account import ServiceAccountCredentials

from apiclient.http import MediaIoBaseDownload

import io

Scopes=" ".join(['https://www.googleapis.com/auth/drive.file',
                 'https://www.googleapis.com/auth/drive.metadata',
                 'https://www.googleapis.com/auth/drive.readonly'])

def export_text(id,
                clientfile,
                scopes=Scopes):
    creds=ServiceAccountCredentials.from_json_keyfile_name(clientfile,
                                                           scopes)
    service=google.build('drive', 'v3', credentials=creds)
    request=service.files().export_media(fileId=id,
                                         mimeType='text/plain')
    buf=io.BytesIO()
    downloader, done = MediaIoBaseDownload(buf, request), False
    while done is False:
        status, done = downloader.next_chunk()
        destfilename="tmp/%s.txt" % id
    return buf.getvalue().decode("utf-8")

if __name__=='__main__':
    print (export_text(id="#{redacted}",
                       clientfile="path/to/service/account.json"))

但是当我 运行 它用于相同的 id 时,我得到以下 -

googleapiclient.errors.HttpError: <HttpError 404 when requesting https://www.googleapis.com/drive/v3/files/#{redacted}/export?mimeType=text%2Fplain&alt=media returned "File not found: #{redacted}.">

感觉服务帐户脚本正在通过身份验证步骤(即服务帐户凭据没问题)但是在尝试获取文件时失败了 - 很奇怪,因为我可以使用 OAuth 版本很好地获取它:/

考虑到 OAuth 客户端版本显然适用于相同的服务帐户版本,关于可能导致此 404 错误的任何想法 id?

在拨打电话之前,先File.list查看服务帐户可以访问哪些文件。对服务帐户无权访问的文件执行 file.get 将导致文件未找到错误。请记住,服务帐户不是您,它有自己的 google 驱动器帐户。您要访问的任何文件都需要上传到其帐户或与服务帐户共享。

如果 file.list 失败,那么它会向我提示授权有问题,您应该确保服务帐户可以访问客户端文件,也许是它找不到的那个文件。

正在授予服务帐户访问权限

在您的个人 google 驱动器帐户上创建一个目录。获取服务帐户电子邮件地址,可以在您下载的密钥文件中找到它,其中有一个@。然后在您的驱动器帐户上与服务帐户共享该目录,就像您与任何其他用户共享一样。

  • 将文件添加到该目录可能会或可能不会自动授予服务帐户对它们的访问权限,这很麻烦,您可能还需要与服务帐户共享该文件。
  • 记得让服务帐户在上传文件时授予您的个人帐户权限,文件将成为所有者。

答案:

您需要与服务帐户共享您的文件。

更多信息:

与处理任何文件一样,您需要授予用户明确的权限才能查看它。由于服务帐户对您来说是一个单独的实体,因此这对他们也适用。

使用文件共享设置(您可以通过 right-clicking 文件并点击 Share 在云端硬盘 UI 中执行此操作),为服务帐户的电子邮件地址提供正确的权限(read/write)。服务帐户的电子邮件地址格式为:

service-account-name@project-id.iam.gserviceaccount.com