如何在AWS Lambda函数中授权Google Drive API并解决只读文件系统问题?
问题描述
我正在编写一个与API网关集成的Lambda函数,需连接Google Drive。本地使用googleapiclient库的代码可正常运行,通过OAuth2及credentials.json、client_secret.json文件实现Google Drive连接。但将这些文件上传至Lambda后,代码报错提示文件系统只读,且代码正尝试写入credentials.json。请问如何解决该问题?能否将文件存储在S3等远程位置?或有无办法让认证代码无需修改credentials.json?
原代码:
from googleapiclient import discovery, errors from googleapiclient.http import MediaIoBaseDownload, MediaFileUpload from httplib2 import Http from oauth2client import client, file, tools credentials_file_path = './credentials.json' clientsecret_file_path = './client_secret.json' SCOPE = 'https://www.googleapis.com/auth/drive' store = file.Storage(credentials_file_path) credentials = store.get() if not credentials or credentials.invalid: flow = client.flow_from_clientsecrets(clientsecret_file_path, SCOPE) credentials = tools.run_flow(flow, store) http = credentials.authorize(Http()) drive = discovery.build('drive', 'v3', http=http)
解决方案
核心原因
Lambda的文件系统仅/tmp目录具备可写权限,其余目录均为只读。你的代码中file.Storage(credentials_file_path)会尝试将refresh token写入credentials.json,但默认路径不在/tmp,因此触发只读错误。
方案1:临时存储至Lambda的/tmp目录
将credentials.json的存储路径改为/tmp/credentials.json,该目录支持写入,但注意:
- 函数执行结束后
/tmp目录会被清理 - 并发执行的Lambda实例拥有独立的
/tmp,无法共享credentials
修改后的代码片段:
credentials_file_path = '/tmp/credentials.json' clientsecret_file_path = './client_secret.json' # client_secret.json无需修改,可放在代码包根目录
方案2:S3持久化存储credentials(推荐)
如果需要在多次函数调用间复用refresh token,可将credentials.json存储在S3,每次调用时下载至/tmp,更新后再上传回S3:
前置准备:
- 将初始credentials.json上传至目标S3桶
- 为Lambda执行角色添加S3的
GetObject和PutObject权限
修改后的代码:
import boto3 from googleapiclient import discovery, errors from googleapiclient.http import MediaIoBaseDownload, MediaFileUpload from httplib2 import Http from oauth2client import client, file, tools # 配置S3信息 S3_BUCKET = 'your-bucket-name' S3_CRED_KEY = 'credentials.json' LOCAL_CRED_PATH = '/tmp/credentials.json' clientsecret_file_path = './client_secret.json' SCOPE = 'https://www.googleapis.com/auth/drive' # 初始化S3客户端 s3 = boto3.client('s3') # 从S3下载credentials到/tmp,不存在则创建空文件 try: s3.download_file(S3_BUCKET, S3_CRED_KEY, LOCAL_CRED_PATH) except s3.exceptions.NoSuchKey: with open(LOCAL_CRED_PATH, 'w') as f: pass store = file.Storage(LOCAL_CRED_PATH) credentials = store.get() if not credentials or credentials.invalid: flow = client.flow_from_clientsecrets(clientsecret_file_path, SCOPE) credentials = tools.run_flow(flow, store) # 将更新后的credentials上传回S3 s3.upload_file(LOCAL_CRED_PATH, S3_BUCKET, S3_CRED_KEY) http = credentials.authorize(Http()) drive = discovery.build('drive', 'v3', http=http)
方案3:使用服务账号替代用户OAuth2授权(无需写入credentials)
如果业务场景允许(比如访问服务账号自身Drive或共享文件),可以用服务账号认证彻底规避credentials写入问题:
前置准备:
- 在Google Cloud控制台创建服务账号,下载服务账号密钥JSON文件
- 将密钥文件放入Lambda代码包,或存储在S3/Secrets Manager
- 给服务账号分配对应的Drive权限(如共享文件给服务账号邮箱)
修改后的代码:
from google.oauth2 import service_account from googleapiclient.discovery import build SCOPES = ['https://www.googleapis.com/auth/drive'] SERVICE_ACCOUNT_FILE = './service-account-key.json' # 放在代码包根目录,只读即可 credentials = service_account.Credentials.from_service_account_file( SERVICE_ACCOUNT_FILE, scopes=SCOPES) drive = build('drive', 'v3', credentials=credentials)
注意:若需访问Google Workspace用户的Drive,需额外启用域范围授权。
内容的提问来源于stack exchange,提问作者Sarah
相关产品推荐
相关产品推荐

