如何修复Google Apps Script向FastAPI发送PDF的POST请求报错
修复Google Apps Script调用FastAPI文件上传接口的问题
问题场景
通过Google Apps Script(GAS)将Google Drive中的PDF文件发送到自研FastAPI接口,接口负责提取PDF文本生成摘要并返回HTML文件。Swagger UI调用接口正常,但GAS调用时提示uploaded_file字段缺失。
现有代码
GAS代码
function process_PDF_file() { const PDF_fileId = "###"; // Google Drive中目标PDF的文件ID const url = "###"; // API接口地址 const payload = { file: DriveApp.getFileById(PDF_fileId).getBlob() }; let config = { muteHttpExceptions: true, payload }; let response = UrlFetchApp.fetch(url, config); var data = JSON.parse(response.getContentText()); Logger.log(data); }
FastAPI路由代码
from fastapi import FastAPI, File app = FastAPI() @app.get("/") async def root(): return {"message": "Hello Summary Creator"} @app.post("/file") async def endpoint(uploaded_file: bytes = File()): content = uploaded_file filename = "temp.pdf" with open(filename, "wb") as f: f.write(content) main_routine() # 自定义的摘要生成逻辑 return {"Summary": f"{summary_download}"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000)
报错信息
10:56:22 AM Info {detail=[{type=missing, url=https://errors.pydantic.dev/2.9/v/missing, input=null, loc=[body, uploaded_file], msg=Field required}]}
修复方案
核心问题
FastAPI的File()参数要求请求采用multipart/form-data格式,且字段名必须与接口参数名uploaded_file完全匹配。原GAS代码存在三个关键问题:
- 默认使用GET请求,而接口是POST路由
- 表单字段名用了
file,与接口要求的uploaded_file不匹配 - 未正确配置
multipart/form-data请求头
修正后的GAS代码
function process_PDF_file() { const PDF_fileId = "###"; const url = "###"; // 获取PDF blob并指定文件名(可选但推荐) const pdfBlob = DriveApp.getFileById(PDF_fileId).getBlob().setName("uploaded.pdf"); const payload = { uploaded_file: pdfBlob // 字段名与FastAPI参数名一致 }; const config = { method: "POST", // 明确指定POST方法 muteHttpExceptions: true, contentType: false, // 让GAS自动处理multipart/form-data格式 payload: payload }; const response = UrlFetchApp.fetch(url, config); const data = JSON.parse(response.getContentText()); Logger.log(data); }
可选的FastAPI接口优化
为了更规范地处理文件上传(包括获取文件名等元数据),可以将接口参数改为UploadFile类型:
from fastapi import FastAPI, File, UploadFile app = FastAPI() @app.get("/") async def root(): return {"message": "Hello Summary Creator"} @app.post("/file") async def endpoint(uploaded_file: UploadFile = File(...)): // 读取文件内容 content = await uploaded_file.read() // 使用上传的文件名,或自定义 filename = uploaded_file.filename with open(filename, "wb") as f: f.write(content) main_routine() return {"Summary": summary_download} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="127.0.0.1", port=8000)
内容的提问来源于stack exchange,提问作者Mike B.
相关产品推荐
相关产品推荐

