Firebase Cloud Functions生成CSV存到Cloud Storage并获取下载URL报错求助
问题解决方法
报错The action is not provided or invalid的核心根因:getSignedUrl方法的action参数不支持download取值,Google Cloud Storage Node.js SDK规定读取/下载场景对应的action有效值为read。
具体修正步骤
- 修正
getSignedUrl参数配置
除了替换action取值,建议将expires参数改为毫秒时间戳,避免不同环境下日期格式解析异常。 - 修正文件路径匹配错误
上传时使用的目标路径是filePath,后续获取文件实例时要使用相同路径,不要混用fileName变量避免路径不匹配。 - 补充CSV内容写入临时文件的逻辑
当前代码仅生成了CSV字符串,未写入到tempLocalFile对应的本地临时文件,会导致上传失败。可以使用Node.js内置的fs和os模块处理临时文件写入。 - 优化不必要的异步逻辑
原代码中用Promise.all包裹report.shifts.map(async ...)是不必要的,CSV内容拼接为同步操作,直接用forEach遍历即可,减少不必要的异步开销。 - 统一异步逻辑处理
不要混用async/await和then回调,避免云函数在未获取到签名URL时提前结束请求返回。
完整修正后代码示例
const functions = require("firebase-functions"); const cors = require("cors")({ origin: true }); const { Storage } = require("@google-cloud/storage"); const os = require("os"); const fs = require("fs/promises"); const path = require("path"); const { v4: uuidv4 } = require("uuid"); // 初始化Storage实例 const storage = new Storage(); // 替换为你的Cloud Storage桶名称,默认格式为 <项目ID>.appspot.com const bucket = storage.bucket("你的存储桶名称"); exports.createCSV = functions.https.onRequest((req, res) => { return cors(req, res, async () => { try { let csv = ""; const { report } = req.body; const { location } = report; const { postcode, streetAddress, city, state } = location.address; // 此处补全firstName、lastName的正确取值逻辑,原代码缺失该部分取值 const { firstName, lastName } = report; // 生成CSV表头 const header = [ "First Name", "Last Name", "Postcode", "Street Address", "City", "State", "Start", "End", "Status", ]; csv += header.join(";") + "\n"; // 生成CSV行内容 report.shifts.forEach((shift) => { const row = [ firstName, lastName, postcode, streetAddress, city, state, shift.startStr, shift.endStr, shift.status, ]; csv += row.join(";") + "\n"; }); // 将CSV内容写入本地临时文件 const tempLocalFile = path.join(os.tmpdir(), "temp_report.csv"); await fs.writeFile(tempLocalFile, csv); // 上传文件到Cloud Storage const filePath = `reports/${uuidv4()}.csv`; // 自定义文件在存储桶中的存储路径 await bucket.upload(tempLocalFile, { destination: filePath, metadata: { metadata: { firebaseStorageDownloadTokens: uuidv4(), }, }, }); // 获取签名下载URL const file = bucket.file(filePath); const [signedUrl] = await file.getSignedUrl({ action: "read", // 下载场景固定填read expires: Date.now() + 1000 * 60 * 60 * 24 * 365 * 100, // 示例为100年有效期,可自行调整 }); // 清理临时文件,避免占用运行环境存储空间 await fs.unlink(tempLocalFile); // 返回下载URL给前端 res.status(200).json({ downloadUrl: signedUrl }); } catch (error) { console.error("生成CSV失败:", error); res.status(500).json({ error: "生成CSV失败,请重试" }); } }); });
额外注意事项
如果部署后出现权限类报错,确认云函数默认服务账号<项目ID>@appspot.gserviceaccount.com拥有存储桶的读写权限即可,默认配置下该权限已自动开通。
内容的提问来源于stack exchange,提问作者Sy Quoc
相关产品推荐
相关产品推荐

