如何在Cloud Functions存储触发器中获取经过身份验证的Firebase用户的uid

时间:2018-07-04 17:00:27

标签: android firebase google-cloud-firestore google-cloud-functions firebase-storage

背景:我正在将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数据库中此登录用户的文档下? / p>

3 个答案:

答案 0 :(得分:3)

当前,使用Cloud Storage触发器,您无权访问经过身份验证的用户信息。要解决此问题,您必须执行一些操作,例如将uid嵌入文件的路径中,或将uid作为元数据添加到文件上传中。

答案 1 :(得分:0)

我有一个类似的问题,我想将用户上传的图像与他/她的 uid 相关联。我找到了一个优雅的解决方案,它不一定需要在文件路径中插入 uid,甚至不需要将其作为元数据添加到文件上传中。实际上,uid 通过标准的 idToken 编码安全地传输到数据库。此示例采用了生成缩略图云函数示例(找到 here)的修改版本,我相信问题的作者正在使用/暗指。以下是步骤:

客户端:

  1. 创建一个在用户上传图像后运行的触发器函数 - 该函数将直接调用云函数(通过 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); 
  })
}
  1. 创建图片上传函数——这是一个标准的文件上传处理函数;您可以将 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));
}

服务器端:

  1. 创建一个云函数,将图像转换为调整大小的缩略图并生成一个签名 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_WIDTH}x${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);
  }
};

答案 2 :(得分:0)

if (firebase.auth().currentUser !== null) 
        console.log("user id: " + firebase.auth().currentUser.uid);

获取用户用户 ID 的简单方法。