Google App Engine通过/tmp/读取文件报错[Errno 2]无对应文件或目录如何解决?
问题根源
- Google App Engine(GAE)的
/tmp目录是实例级临时存储,仅在当前实例运行期间有效,部署阶段不会将本地的token_sheets_v4.pickle上传到该路径,自然读取不到文件。 - 你配置的
app.yaml的handlers规则存在严重错误:把根路径/映射为静态pickle文件,会导致所有根路径请求直接返回文件下载,完全绕开了Flask应用逻辑。 - 代码本身存在两处缺陷:
pickle_file变量未提前定义,读写路径不一致,一会硬编码/tmp路径一会用未定义变量flow.run_local_server()是桌面端OAuth授权流程,依赖本地浏览器完成授权交互,在GAE服务端环境运行会直接失败,无法完成授权。
解决方案(推荐用方案1,适配服务端部署场景)
方案1:使用服务账号授权
- 进入Google Cloud控制台「API和服务」-「凭据」页面,创建服务账号,下载JSON格式的密钥文件,命名为
service_account.json,和Flask启动文件(main.py)放在同一目录。 - 打开你要操作的Google Sheet,点击「共享」,输入服务账号的邮箱地址,授予对应查看/编辑权限。
- 替换
Create_Service函数逻辑,改用服务账号授权,无需本地生成pickle文件、无需手动授权:
import os from google.oauth2 import service_account from googleapiclient.discovery import build def Create_Service(api_name, api_version, *scopes): SCOPES = [scope for scope in scopes[0]] # 读取同目录下的服务账号密钥文件 creds = service_account.Credentials.from_service_account_file( os.path.join(os.path.dirname(__file__), 'service_account.json'), scopes=SCOPES ) try: service = build(api_name, api_version, credentials=creds) print(f'{api_name} service created successfully') return service except Exception as e: print('Unable to connect.') print(e) return None
- 修正
app.yaml配置,删除错误的handlers规则,保留基础配置即可:
runtime: python39 # 替换为你实际使用的Python版本 entrypoint: gunicorn -b :$PORT main:app
- 部署时确保
service_account.json在上传文件列表中。
方案2:保留原有OAuth pickle文件的适配方案
如果必须使用原有生成的token_sheets_v4.pickle,按以下步骤调整:
- 本地完成授权,确保生成的
token_sheets_v4.pickle可用,将文件放到Flask应用根目录(和main.py同目录)。 - 修正代码路径逻辑,不要读写
/tmp目录,删除服务端无法运行的授权流程:
def Create_Service(client_secret_file, api_name, api_version, *scopes): CLIENT_SECRET_FILE = client_secret_file API_SERVICE_NAME = api_name API_VERSION = api_version SCOPES = [scope for scope in scopes[0]] cred = None # 统一pickle文件路径,指向应用根目录的文件 pickle_file = os.path.join(os.path.dirname(__file__), 'token_sheets_v4.pickle') if os.path.exists(pickle_file): with open(pickle_file, 'rb') as token: cred = pickle.load(token) # 服务端不需要再走授权流程,本地已经生成可用的cred,删掉下面这段flow相关逻辑 # if not cred or not cred.valid: # if cred and cred.expired and cred.refresh_token: # cred.refresh(Request()) # else: # flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES) # cred = flow.run_local_server() # with open(pickle_file, 'wb') as token: # pickle.dump(cred, token) try: service = build(API_SERVICE_NAME, API_VERSION, credentials=cred) print(API_SERVICE_NAME, 'service created successfully') return service except Exception as e: print('Unable to connect.') print(e) return None
- 同样删除
app.yaml中错误的handlers静态文件配置,确保请求正常转发到Flask应用。 - 部署时将
token_sheets_v4.pickle和代码一起上传。注意该方案下令牌过期后需要本地重新生成再部署,维护成本更高。
内容的提问来源于stack exchange,提问作者junietoc
相关产品推荐
相关产品推荐

