Firebase云函数验证Storage文件数据并存储至实时数据库的实现方案
实现Firebase云函数中的文件验证逻辑
我来帮你梳理并实现这些验证逻辑,核心思路是通过传入的fileRef从Firebase Storage获取真实的文件元数据,再逐一与前端传入的字段做比对,确保数据的真实性。以下是完整的实现方案:
云函数完整代码
首先确保你已经在云函数项目中初始化了firebase-admin,然后编写如下的saveFileData函数:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.saveFileData = functions.https.onCall(async (data, context) => { // 1. 验证调用者身份(可选但推荐,确保只有登录用户能调用) if (!context.auth) { throw new functions.https.HttpsError( "unauthenticated", "用户未登录,无法调用此函数" ); } // 2. 提取并检查传入的必要参数 const { fileId, fileRef, fileData } = data; if (!fileRef || !fileData || !fileData.FileName || !fileData.Size || !fileData.DownloadURL || !fileData.UploadDate) { throw new functions.https.HttpsError( "invalid-argument", "缺少必要的验证字段" ); } try { // 3. 获取Storage中对应文件的引用和元数据 const storageRef = admin.storage().ref(fileRef); const metadata = await storageRef.getMetadata(); // 4. 逐一执行验证逻辑 // 验证fileRef指向的文件存在(getMetadata成功即说明存在) // 验证FileName匹配 if (metadata.name !== fileData.FileName) { throw new functions.https.HttpsError( "failed-precondition", "文件名与Storage中的文件不匹配" ); } // 验证Size匹配(注意:前端传入的Size要确保是数字,元数据的size是Number类型) if (metadata.size !== parseInt(fileData.Size, 10)) { throw new functions.https.HttpsError( "failed-precondition", "文件大小与Storage中的文件不匹配" ); } // 验证UploadDate匹配(前端是toString()后的字符串,直接比对即可) if (metadata.timeCreated !== fileData.UploadDate) { throw new functions.https.HttpsError( "failed-precondition", "上传日期与Storage中的文件不匹配" ); } // 验证DownloadURL有效:获取真实的下载链接并比对 const actualDownloadURL = await storageRef.getDownloadURL(); if (actualDownloadURL !== fileData.DownloadURL) { throw new functions.https.HttpsError( "failed-precondition", "下载链接无效或不匹配" ); } // 5. 所有验证通过,将数据存入实时数据库 const dbRef = admin.database().ref(`Files/${context.auth.uid}/${fileId}`); await dbRef.set({ ...fileData, fileRef: fileRef, fileId: fileId }); return { success: true, message: "文件数据已成功保存" }; } catch (error) { // 处理Storage操作的错误,比如文件不存在 if (error.code === "storage/object-not-found") { throw new functions.https.HttpsError( "not-found", "指定的文件在Storage中不存在" ); } // 抛出其他错误 throw new functions.https.HttpsError( "internal", "验证或保存数据时发生错误", error.message ); } });
关键验证点说明
- 文件存在验证:调用
storageRef.getMetadata()如果失败(比如文件不存在),会抛出storage/object-not-found错误,我们可以捕获这个错误并返回给前端。 - 文件名匹配:直接比对元数据中的
name字段和前端传入的FileName,确保文件名一致。 - 文件大小匹配:注意前端传入的
Size可能是字符串,所以需要用parseInt转成数字后再和元数据的size(数字类型)比对。 - 上传日期匹配:前端是把
metadata.timeCreated直接转成字符串传入的,所以直接比对字符串即可,无需额外处理格式。 - 下载链接验证:最可靠的方式是通过Storage引用重新获取真实的下载链接,再和前端传入的URL比对,避免前端传入伪造的链接。
前端调用的小优化
你的前端代码可以稍微简化一下,不需要重复创建Storage引用:
var fileRef = `Files/${user.uid}/${fileId}/${file.name}`; const storage = firebase.storage().ref(fileRef); // 上传文件逻辑... storage.getMetadata().then(async (metadata) => { const date = metadata.timeCreated.toString(); const url = await storage.getDownloadURL(); // 直接用已有的storage引用,不用重新创建 const saveFileData = functions.httpsCallable('saveFileData'); try { await saveFileData({ fileId: fileId, fileRef: fileRef, fileData: { Uploader: user.uid, Title: title, FileName: file.name, Size: file.size, // 直接用file.size数字,不用转字符串 DownloadURL: url, UploadDate: date, } }); // 处理成功逻辑 } catch (error) { // 处理云函数返回的错误 console.error(error.message); } });
这样前端传入的Size是数字类型,和云函数里的元数据size类型一致,避免转换错误。
内容的提问来源于stack exchange,提问作者Sadman Rizwan
相关产品推荐
相关产品推荐

