如何在 Cloud Functions 存储触发器中获取经过身份验证的 Firebase 用户的 uid
Posted
技术标签:
【中文标题】如何在 Cloud Functions 存储触发器中获取经过身份验证的 Firebase 用户的 uid【英文标题】:How to get the uid of the authenticated Firebase user in a Cloud Functions storage trigger 【发布时间】:2018-12-13 03:58:54 【问题描述】:背景:我正在使用 Firebase Cloud Functions、新的 Firestore 数据库和带有 android 客户端的存储桶。
我想要完成的事情: 当用户将图片上传到存储桶时,我想使用云功能获取存储桶中图像位置的文件路径/链接,并将此字符串作为新文档存储在名为“图片”的新集合下当前登录用户在 Firestore 中的文档。
这样,我可以在 Firestore 中看到每个用户直接上传的图像,并且可以更轻松地将特定用户的图像拉到 Android 客户端。
到目前为止我已完成的工作: 1. 用户首次登录时,会在新的 Firestore 数据库中创建一个用户文档。 2. 登录用户可以上传图片到存储桶。 3.使用Firebase Cloud Functions,我设法获得了存储位置的文件路径/链接如下:
/**
* When a user selects a profile picture on their device during onboarding,
* the image is sent to Firebase Storage from a function running on their device.
* The cloud function below returns the file path of the newly uploaded image.
*/
exports.getImageStorageLocationWhenUploaded = functions.storage.object().onFinalize((object) =>
const filePath = object.name; // File path in the bucket.
const contentType = object.contentType; // File content type.
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/'))
console.log('This is not an image.');
return null;
console.log(filePath);
);
问题:如何使用 Cloud Functions 获取当前登录用户并将该用户上传的图像文件路径/链接作为新文档存储在 Firestore 数据库中的该登录用户文档下?
【问题讨论】:
【参考方案1】:目前,通过 Cloud Storage 触发器,您无法访问经过身份验证的用户信息。要解决这个问题,您必须执行一些操作,例如将 uid 嵌入文件的路径中,或者将 uid 作为元数据添加到文件上传中。
【讨论】:
感谢道格的解释!为了将上传的图像与上传这些图像的用户连接起来,创建与此 uid 命名相同的新存储桶文件夹是否更好?还是我应该像您建议的那样将其添加为元数据? 我会将 uid 放在路径中,以便文件也可以使用 Cloud Storage 安全规则受到保护,以便仅对该用户进行读写。【参考方案2】:我遇到了类似的问题,我想将用户上传的图片与他/她的 uid 相关联。我找到了一个优雅的解决方案,它不一定需要在文件路径中插入 uid,甚至不需要将其作为元数据添加到文件上传中。事实上,uid 是通过标准的 idToken 编码安全地传输到数据库的。此示例使用了生成缩略图云功能示例的修改版本(找到here),我相信该问题的作者正在使用/暗示。步骤如下:
客户端:
-
创建一个触发器函数,一旦用户上传图像就会运行 - 该函数将直接调用云函数(通过 httpsCallable 方法)。云函数将接收用户 uid(idToken 编码)以及您可能希望发送的任何图像元数据。然后,云函数将返回图像的签名 URL。
const generateThumbnail = firebase.functions().httpsCallable('generateThumbnail');
const getImageUrl = (file) =>
firebase.auth().currentUser.getIdToken(true)
.then((idToken) => generateThumbnail(
idToken,
imgName: file.name,
contentType: file.type
))
.then((data) =>
// Here you can save your image url to the app store, etc.
// An example of a store action:
// setImageUrl(data.data.url);
)
.catch((error) =>
console.log(error);
)
-
创建图片上传函数——这是一个标准的文件上传处理函数;您可以将 uid 部分设置为图像的存储文件路径位置(如果需要),但也可以在图像上传后触发 firebase 功能。这可以使用
on
方法的第三个参数来实现。将上面的触发函数作为第三个参数包含在此处。
// Function triggered on file import status change from the <input /> tag
const createThumbnail = (e) =>
e.persist();
let file = e.target.files[0];
// If you are using a non default storage bucket use this
// let storage = firebase.app().storage('gs://your_non_default_storage_bucket');
// If you are using the default storage bucket use this
let storage = firebase.storage();
// You can add the uid in the image file path store location but this is optional
let storageRef = storage.ref(`$uid/thumbnail/$file.name`);
storageRef.put(file).on('state_changed', (snapshot) => , (error) =>
console.log('Something went wrong! ', error);
, getImageUrl(file));
服务器端:
-
创建云函数将图像转换为调整大小的缩略图并生成签名 URL - 此云函数从存储中获取图像,将其转换为缩略图(基本上减小了其尺寸但保持初始纵横比)使用 ImageMagick(默认情况下安装在所有云函数实例上)。然后它会生成图像位置的签名 URL 并将其返回给客户端。
// Import your admin object with the initialized app credentials
const mkdirp = require('mkdirp');
const spawn = require('child-process-promise').spawn;
const path = require('path');
const os = require('os');
const fs = require('fs');
// Max height and width of the thumbnail in pixels.
const THUMB_MAX_HEIGHT = 25;
const THUMB_MAX_WIDTH = 125;
// Thumbnail prefix added to file names.
const THUMB_PREFIX = 'thumb_';
async function generateThumbnail(data)
// Get the user uid from IdToken
const idToken, imgName, contentType = data;
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
const uid = decodedIdToken.uid;
// File and directory paths.
const filePath = `$uid/thumbnail/$imgName`;
const fileDir = path.dirname(filePath);
const fileName = path.basename(filePath);
const thumbFilePath = path.normalize(path.join(fileDir, `$THUMB_PREFIX$fileName`));
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
const tempLocalThumbFile = path.join(os.tmpdir(), thumbFilePath);
// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith('image/'))
return console.log('This is not an image.');
// Exit if the image is already a thumbnail.
if (fileName.startsWith(THUMB_PREFIX))
return console.log('Already a Thumbnail.');
// Cloud Storage files.
const bucket = initDb.storage().bucket('your_bucket_if_non_default');
const originalFile = bucket.file(filePath);
// Create the temp directory where the storage file will be downloaded.
// But first check to see if it does not already exists
if (!fs.existsSync(tempLocalDir)) await mkdirp(tempLocalDir);
// Download original image file from bucket.
await originalFile.download( destination: tempLocalFile );
console.log('The file has been downloaded to', tempLocalFile);
// Delete the original image file as it is not needed any more
await originalFile.delete();
console.log('Delete the original file as it is not needed any more');
// Generate a thumbnail using ImageMagick.
await spawn('convert', [ tempLocalFile, '-thumbnail',
`$THUMB_MAX_WIDTHx$THUMB_MAX_HEIGHT>`, tempLocalThumbFile],
capture: ['stdout', 'stderr']
);
console.log('Thumbnail created at', tempLocalThumbFile);
// Uploading the Thumbnail.
const url = await uploadLocalFileToStorage(tempLocalThumbFile, thumbFilePath,
contentType);
console.log('Thumbnail uploaded to Storage at', thumbFilePath);
// Once the image has been uploaded delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile);
fs.unlinkSync(tempLocalThumbFile);
// Delete the uid folder from temp/pdf folder
fs.rmdirSync(tempLocalDir);
await admin.database().ref(`users/$uid/uploaded_images`).update( logoUrl: url[0] );
return url: url[0] ;
// Upload local file to storage
exports.uploadLocalFileToStorage = async (tempFilePath, storageFilePath,
contentType, customBucket = false) =>
let bucket = initDb.storage().bucket();
if (customBucket) bucket = initDb.storage().bucket(customBucket);
const file = bucket.file(storageFilePath);
try
// Check if file already exists; if it does delete it
const exists = await file.exists();
if (exists[0]) await file.delete();
// Upload local file to the bucket
await bucket.upload(tempFilePath,
destination: storageFilePath,
metadata: cacheControl: 'public, max-age=31536000', contentType
);
const currentDate = new Date();
const timeStamp = currentDate.getTime();
const newDate = new Date(timeStamp + 600000);
const result = await file.getSignedUrl(
action: 'read',
expires: newDate
);
return result;
catch (e)
throw new Error("uploadLocalFileToStorage failed: " + e);
;
【讨论】:
【参考方案3】:if (firebase.auth().currentUser !== null)
console.log("user id: " + firebase.auth().currentUser.uid);
获取用户用户 ID 的简单方法。
【讨论】:
以上是关于如何在 Cloud Functions 存储触发器中获取经过身份验证的 Firebase 用户的 uid的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Cloud Functions 连接 Google Cloud SQL?
从 Cloud Functions for Firebase 获取当前用户 ID(PubSub 触发器)