Firebase 的雲函數

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

一、概述

在此 Codelab 中,您將了解如何使用 Firebase SDK for Google Cloud Functions 來改進 Chat Web 應用,以及如何使用 Cloud Functions 向 Chat 應用的用戶發送通知。

3b1284f5144b54f6.png

你會學到什麼

  • 使用 Firebase SDK 創建 Google Cloud Functions。
  • 根據 Auth、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
  • 將 Firebase 雲消息傳遞支持添加到您的 Web 應用程序。

你需要什麼

  • 一張信用卡。 Cloud Functions for Firebase 需要 Firebase Blaze 計劃,這意味著您必須使用信用卡對您的 Firebase 項目啟用結算功能。
  • 您選擇的 IDE/文本編輯器,例如WebStormAtomSublime
  • 一個安裝了 NodeJS v9 的終端來運行 shell 命令。
  • 瀏覽器,例如 Chrome。
  • 示例代碼。請參閱下一步。

2.獲取示例代碼

從命令行克隆GitHub 存儲庫

git clone https://github.com/firebase/friendlychat

導入入門應用

使用您的 IDE,打開或導入android_studio_folder.png示例代碼目錄中的cloud-functions-start目錄。此目錄包含 codelab 的起始代碼,其中包含一個功能齊全的 Chat Web App。

3. 創建一個 Firebase 項目並設置您的應用

創建項目

Firebase Console中,單擊Add Project並將其命名為FriendlyChat

單擊創建項目

升級到 Blaze 計劃

要使用 Cloud Functions for Firebase,您必須將您的 Firebase 項目升級到Blaze 計費方案。這將要求您將信用卡或其他計費方式添加到您的 Google Cloud 帳戶。

所有 Firebase 項目,包括 Blaze 計劃中的項目,仍然可以使用 Cloud Functions 的免費使用配額。此 Codelab 中概述的步驟將在免費層使用限制範圍內。但是,您會看到來自 Cloud Storage 的小額費用(約 0.03 美元),用於託管您的 Cloud Functions 構建映像。

如果您無法使用信用卡或對繼續使用 Blaze 計劃感到不舒服,請考慮使用Firebase Emulator Suite ,它允許您在本地計算機上免費模擬 Cloud Functions。

啟用谷歌身份驗證

為了讓用戶登錄應用程序,我們將使用需要啟用的 Google 身份驗證。

在 Firebase 控制台中,打開構建部分 >身份驗證>登錄方法選項卡(或單擊此處前往那裡)。然後,啟用Google Sign-in Provider 並點擊Save 。這將允許用戶使用他們的 Google 帳戶登錄網絡應用程序。

此外,您可以隨意將您的應用程序的公開名稱設置為Friendly Chat

8290061806aacb46.png

啟用雲存儲

該應用程序使用雲存儲上傳圖片。要在您的 Firebase 項目上啟用 Cloud Storage,請訪問存儲部分並點擊開始按鈕。完成那裡的步驟,對於 Cloud Storage 位置,將使用一個默認值。之後單擊完成

添加網絡應用

在 Firebase 控制台上,添加一個 Web 應用。為此,請轉到項目設置並向下滾動到添加應用程序。選擇 web 作為平台並選中設置 Firebase Hosting 的複選框,然後註冊應用程序並單擊Next進行其餘步驟,最後單擊Continue to console

4. 安裝 Firebase 命令行界面

Firebase 命令行界面 (CLI) 將允許您在本地提供 Web 應用程序並部署您的 Web 應用程序和 Cloud Functions。

要安裝或升級 CLI,請運行以下 npm 命令:

npm -g install firebase-tools

要驗證 CLI 是否已正確安裝,請打開控制台並運行:

firebase --version

確保 Firebase CLI 的版本高於4.0.0 ,以便它具有 Cloud Functions 所需的所有最新功能。如果沒有,請運行npm install -g firebase-tools進行升級,如上所示。

通過運行以下命令授權 Firebase CLI:

firebase login

確保您位於cloud-functions-start目錄中,然後設置 Firebase CLI 以使用您的 Firebase 項目:

firebase use --add

接下來,選擇您的項目 ID 並按照說明進行操作。出現提示時,您可以選擇任何別名,例如codelab

5. 部署並運行 Web 應用

現在您已經導入並配置了您的項目,您已經準備好第一次運行 Web 應用程序了!打開終端窗口,導航到cloud-functions-start文件夾,然後使用以下命令將 Web 應用部署到 Firebase 託管:

firebase deploy --except functions

這是您應該看到的控制台輸出:

i deploying database, storage, hosting
✔  database: rules ready to deploy.
i  storage: checking rules for compilation errors...
✔  storage: rules file compiled successfully
i  hosting: preparing ./ directory for upload...
✔  hosting: ./ folder uploaded successfully
✔ storage: rules file compiled successfully
✔ hosting: 8 files uploaded successfully
i starting release process (may take several minutes)...

✔ Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com

打開網絡應用

最後一行應顯示託管 URL。現在應該從此 URL 提供 Web 應用程序,其格式應為 https://<project-id>.firebaseapp.com。打開它。您應該會看到聊天應用程序的功能 UI。

使用SIGN-IN WITH GOOGLE按鈕登錄應用程序,然後隨意添加一些消息和發布圖片:

3b1284f5144b54f6.png

如果您在新瀏覽器上首次登錄應用程序,請確保在出現提示時允許通知: 8b9d0c66dc36153d.png

我們需要稍後啟用通知。

如果您不小心點擊了Block ,您可以通過點擊 Chrome Omnibar 中 URL 左側的🔒 Secure按鈕並切換Notifications旁邊的欄來更改此設置:

e926868b0546ed71.png

現在,我們將使用 Firebase SDK for Cloud Functions 添加一些功能。

6.函數目錄

Cloud Functions 允許您輕鬆擁有在雲中運行的代碼,而無需設置服務器。我們將介紹如何構建對 Firebase Auth、Cloud Storage 和 Firebase Firestore 數據庫事件做出反應的函數。讓我們從 Auth 開始。

使用 Firebase SDK for Cloud Functions 時,您的 Functions 代碼將位於functions目錄下(默認情況下)。您的 Functions 代碼也是一個Node.js應用程序,因此需要一個package.json來提供有關您的應用程序的一些信息並列出依賴項。

為了讓您更輕鬆,我們已經創建了您的代碼所在的functions/index.js文件。在繼續之前,請隨意檢查此文件。

cd functions
ls

如果您不熟悉Node.js ,那麼在繼續 Codelab 之前了解更多信息會很有幫助。

package.json文件已經列出了兩個必需的依賴項: Firebase SDK for Cloud FunctionsFirebase Admin SDK 。要在本地安裝它們,請轉到functions文件夾並運行:

npm install

現在讓我們看一下index.js文件:

index.js

/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 * ...
 */

// TODO(DEVELOPER): Import the Cloud Functions for Firebase and the Firebase Admin modules here.

// TODO(DEVELOPER): Write the addWelcomeMessage Function here.

// TODO(DEVELOPER): Write the blurImages Function here.

// TODO(DEVELOPER): Write the sendNotification Function here.

我們將導入所需的模塊,然後編寫三個函數來代替 TODO。讓我們從導入所需的 Node 模塊開始。

7. 導入 Cloud Functions 和 Firebase 管理模塊

在此 Codelab 期間將需要兩個模塊: firebase-functions允許編寫 Cloud Functions 觸發器和日誌,而firebase-admin允許在具有管理員訪問權限的服務器上使用 Firebase 平台來執行操作,例如寫入 Cloud Firestore 或發送 FCM 通知。

index.js文件中,將第一個TODO替換為以下內容:

index.js

/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 * ...
 */

// Import the Firebase SDK for Google Cloud Functions.
const functions = require('firebase-functions');
// Import and initialize the Firebase Admin SDK.
const admin = require('firebase-admin');
admin.initializeApp();

// TODO(DEVELOPER): Write the addWelcomeMessage Function here.

// TODO(DEVELOPER): Write the blurImages Function here.

// TODO(DEVELOPER): Write the sendNotification Function here.

Firebase Admin SDK 可以在部署到 Cloud Functions 環境或其他 Google Cloud Platform 容器時自動配置,當我們調用不帶參數的admin.initializeApp()時會發生這種情況。

現在,讓我們添加一個在用戶首次登錄聊天應用程序時運行的函數,我們將添加一條聊天消息來歡迎用戶。

8. 歡迎新用戶

聊天消息結構

發佈到 FriendlyChat 聊天源的消息存儲在 Cloud Firestore 中。讓我們看看我們用於消息的數據結構。為此,請在聊天中發布一條新消息,內容為“Hello World”:

11f5a676fbb1a69a.png

這應該顯示為:

fe6d1c020d0744cf.png

在 Firebase 控制台中,單擊構建部分下的Firestore 數據庫。您應該看到消息集合和一個包含您編寫的消息的文檔:

442c9c10b5e2b245.png

如您所見,聊天消息作為文檔存儲在 Cloud Firestore 中,並在messages集合中添加了nameprofilePicUrltexttimestamp屬性。

添加歡迎信息

第一個雲函數添加了一條歡迎新用戶加入聊天的消息。為此,我們可以使用觸發器functions.auth().onCreate ,每次用戶首次登錄 Firebase 應用程序時都會運行該函數。將addWelcomeMessages函數添加到您的index.js文件中:

index.js

// Adds a message that welcomes new users into the chat.
exports.addWelcomeMessages = functions.auth.user().onCreate(async (user) => {
  functions.logger.log('A new user signed in for the first time.');
  const fullName = user.displayName || 'Anonymous';

  // Saves the new welcome message into the database
  // which then displays it in the FriendlyChat clients.
  await admin.firestore().collection('messages').add({
    name: 'Firebase Bot',
    profilePicUrl: '/images/firebase-logo.png', // Firebase logo
    text: `${fullName} signed in for the first time! Welcome!`,
    timestamp: admin.firestore.FieldValue.serverTimestamp(),
  });
  functions.logger.log('Welcome message written to database.');
});

將此函數添加到特殊的exports對像是 Node 使該函數可在當前文件之外訪問的方法,並且是 Cloud Functions 所必需的。

在上面的函數中,我們將“Firebase Bot”發布的新歡迎消息添加到聊天消息列表中。我們通過在 Cloud Firestore 中的messages集合上使用add方法來做到這一點,這是存儲聊天消息的地方。

由於這是一個異步操作,我們需要返回Promise來指示 Cloud Firestore 何時完成寫入,這樣 Cloud Functions 就不會執行得太早。

部署雲功能

Cloud Functions 僅在您部署後才會處於活動狀態。為此,請在命令行上運行:

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
⚠  functions: missing necessary APIs. Enabling now...
i  env: ensuring necessary APIs are enabled...
⚠  env: missing necessary APIs. Enabling now...
i  functions: waiting for APIs to activate...
i  env: waiting for APIs to activate...
✔  env: all necessary APIs are enabled
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: creating function addWelcomeMessages...
✔  functions[addWelcomeMessages]: Successful create operation. 
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlypchat-1234/overview

測試功能

成功部署該功能後,您將需要一個首次登錄的用戶。

  1. 使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中打開您的應用。
  2. 對於新用戶,首次使用登錄按鈕在您的應用中登錄。
  • 如果您已經登錄到應用程序,您可以打開Firebase 控制台身份驗證並從用戶列表中刪除您的帳戶。然後,再次登錄。

262535d1b1223c65.png

  1. 登錄後,應自動顯示歡迎消息:

1c70e0d64b23525b.png

9. 圖片審核

用戶可以在聊天中上傳所有類型的圖片,而適度的攻擊性圖片總是很重要,尤其是在公共社交平台上。在 FriendlyChat 中,發佈到聊天中的圖像存儲在Google Cloud Storage中。

借助 Cloud Functions,您可以使用functions.storage().onFinalize觸發器檢測新的圖像上傳。每次在 Cloud Storage 中上傳或修改新文件時都會運行此操作。

要調整圖像,我們將執行以下過程:

  1. 使用Cloud Vision API檢查圖像是否被標記為成人或暴力。
  2. 如果圖像已被標記,請將其下載到正在運行的 Functions 實例上。
  3. 使用ImageMagick模糊圖像。
  4. 將模糊圖像上傳到 Cloud Storage。

啟用 Cloud Vision API

由於我們將在此函數中使用 Google Cloud Vision API,因此您必須在您的 firebase 項目中啟用該 API。點擊此鏈接,然後選擇您的 Firebase 項目並啟用 API:

5c77fee51ec5de49.png

安裝依賴項

為了審核圖片,我們將使用適用於 Node.js 的 Google Cloud Vision 客戶端庫@google-cloud/vision來通過 Cloud Vision API 運行圖片以檢測不合適的圖片。

要將此包安裝到您的 Cloud Functions 應用程序中,請運行以下npm install --save命令。確保從functions目錄執行此操作。

npm install --save @google-cloud/vision@2.4.0

這將在本地安裝包並將它們作為聲明的依賴項添加到您的package.json文件中。

導入和配置依賴項

要導入已安裝的依賴項和我們在本節中需要的一些 Node.js 核心模塊( pathosfs ),請將以下行添加到index.js文件的頂部:

index.js

const Vision = require('@google-cloud/vision');
const vision = new Vision.ImageAnnotatorClient();
const {promisify} = require('util');
const exec = promisify(require('child_process').exec);

const path = require('path');
const os = require('os');
const fs = require('fs');

由於您的函數將在 Google Cloud 環境中運行,因此無需配置 Cloud Storage 和 Cloud Vision 庫:它們將自動配置為使用您的項目。

檢測不當圖像

您將使用functions.storage.onChange Cloud Functions 觸發器,該觸發器會在 Cloud Storage 存儲桶中創建或修改文件或文件夾後立即運行您的代碼。將blurOffensiveImages函數添加到index.js文件中:

index.js

// Checks if uploaded images are flagged as Adult or Violence and if so blurs them.
exports.blurOffensiveImages = functions.runWith({memory: '2GB'}).storage.object().onFinalize(
    async (object) => {
      const imageUri = `gs://${object.bucket}/${object.name}`;
      // Check the image content using the Cloud Vision API.
      const batchAnnotateImagesResponse = await vision.safeSearchDetection(imageUri);
      const safeSearchResult = batchAnnotateImagesResponse[0].safeSearchAnnotation;
      const Likelihood = Vision.protos.google.cloud.vision.v1.Likelihood;
      if (Likelihood[safeSearchResult.adult] >= Likelihood.LIKELY ||
          Likelihood[safeSearchResult.violence] >= Likelihood.LIKELY) {
        functions.logger.log('The image', object.name, 'has been detected as inappropriate.');
        return blurImage(object.name);
      }
      functions.logger.log('The image', object.name, 'has been detected as OK.');
    });

請注意,我們添加了將運行該函數的 Cloud Functions 實例的一些配置。使用.runWith({memory: '2GB'}) ,我們要求實例獲得 2GB 內存而不是默認內存,因為此函數是內存密集型的。

觸發該功能時,圖像會通過 Cloud Vision API 運行,以檢測它是否被標記為成人或暴力。如果基於這些標準檢測到圖像不合適,我們將模糊圖像,這是在blurImage函數中完成的,我們將在接下來看到。

模糊圖像

index.js文件中添加以下blurImage函數:

index.js

// Blurs the given image located in the given bucket using ImageMagick.
async function blurImage(filePath) {
  const tempLocalFile = path.join(os.tmpdir(), path.basename(filePath));
  const messageId = filePath.split(path.sep)[1];
  const bucket = admin.storage().bucket();

  // Download file from bucket.
  await bucket.file(filePath).download({destination: tempLocalFile});
  functions.logger.log('Image has been downloaded to', tempLocalFile);
  // Blur the image using ImageMagick.
  await exec(`convert "${tempLocalFile}" -channel RGBA -blur 0x24 "${tempLocalFile}"`);
  functions.logger.log('Image has been blurred');
  // Uploading the Blurred image back into the bucket.
  await bucket.upload(tempLocalFile, {destination: filePath});
  functions.logger.log('Blurred image has been uploaded to', filePath);
  // Deleting the local file to free up disk space.
  fs.unlinkSync(tempLocalFile);
  functions.logger.log('Deleted local file.');
  // Indicate that the message has been moderated.
  await admin.firestore().collection('messages').doc(messageId).update({moderated: true});
  functions.logger.log('Marked the image as moderated in the database.');
}

在上述函數中,圖像二進製文件是從 Cloud Storage 下載的。然後使用 ImageMagick 的convert工具對圖像進行模糊處理,並將模糊版本重新上傳到存儲桶中。接下來,我們刪除 Cloud Functions 實例上的文件以釋放一些磁盤空間,我們這樣做是因為同一個 Cloud Functions 實例可以重複使用,如果文件不清理,它可能會耗盡磁盤空間。最後,我們在聊天消息中添加一個布爾值,指示圖像已被審核,這將觸發客戶端上消息的刷新。

部署函數

該功能僅在您部署後才處於活動狀態。在命令行上,運行firebase deploy --only functions

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function addWelcomeMessages...
i  functions: creating function blurOffensiveImages...
✔  functions[addWelcomeMessages]: Successful update operation.
✔  functions[blurOffensiveImages]: Successful create operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview

測試功能

函數部署成功後:

  1. 使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中打開您的應用。
  2. 登錄應用程序後,上傳圖片: 4db9fdab56703e4a.png
  3. 選擇你最好的攻擊性圖片上傳(或者你可以使用這個食肉殭屍!),片刻之後,你應該看到你的帖子刷新了一個模糊版本的圖像: 83dd904fbaf97d2b.png

10. 新消息通知

在本節中,您將添加一個雲函數,該函數在發布新消息時向聊天參與者發送通知。

使用Firebase 雲消息傳遞(FCM),您可以跨平台可靠地向用戶發送通知。要向用戶發送通知,您需要他們的 FCM 設備令牌。我們使用的聊天 Web 應用程序在用戶首次在新瀏覽器或設備上打開應用程序時已經從用戶那裡收集了設備令牌。這些令牌存儲在 Cloud Firestore 的fcmTokens集合中。

如果您想了解如何在 Web 應用程序上獲取 FCM 設備令牌,可以通過Firebase Web Codelab了解。

發送通知

要檢測何時發布新消息,您將使用functions.firestore.document().onCreate Cloud Functions 觸發器,該觸發器會在 Cloud Firestore 的給定路徑創建新對象時運行您的代碼。將sendNotifications函數添加到index.js文件中:

index.js

// Sends a notifications to all users when a new message is posted.
exports.sendNotifications = functions.firestore.document('messages/{messageId}').onCreate(
  async (snapshot) => {
    // Notification details.
    const text = snapshot.data().text;
    const payload = {
      notification: {
        title: `${snapshot.data().name} posted ${text ? 'a message' : 'an image'}`,
        body: text ? (text.length <= 100 ? text : text.substring(0, 97) + '...') : '',
        icon: snapshot.data().profilePicUrl || '/images/profile_placeholder.png',
        click_action: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com`,
      }
    };

    // Get the list of device tokens.
    const allTokens = await admin.firestore().collection('fcmTokens').get();
    const tokens = [];
    allTokens.forEach((tokenDoc) => {
      tokens.push(tokenDoc.id);
    });

    if (tokens.length > 0) {
      // Send notifications to all tokens.
      const response = await admin.messaging().sendToDevice(tokens, payload);
      await cleanupTokens(response, tokens);
      functions.logger.log('Notifications have been sent and tokens cleaned up.');
    }
  });

在上面的函數中,我們從 Cloud Firestore 數據庫中收集所有用戶的設備令牌,並使用admin.messaging().sendToDevice函數向每個用戶發送通知。

清理令牌

最後,我們要刪除不再有效的令牌。當瀏覽器或設備不再使用我們曾經從用戶那裡獲得的令牌時,就會發生這種情況。例如,如果用戶撤銷了瀏覽器會話的通知權限,就會發生這種情況。為此,請在index.js文件中添加以下cleanupTokens函數:

index.js

// Cleans up the tokens that are no longer valid.
function cleanupTokens(response, tokens) {
 // For each notification we check if there was an error.
 const tokensDelete = [];
 response.results.forEach((result, index) => {
   const error = result.error;
   if (error) {
     functions.logger.error('Failure sending notification to', tokens[index], error);
     // Cleanup the tokens that are not registered anymore.
     if (error.code === 'messaging/invalid-registration-token' ||
         error.code === 'messaging/registration-token-not-registered') {
       const deleteTask = admin.firestore().collection('fcmTokens').doc(tokens[index]).delete();
       tokensDelete.push(deleteTask);
     }
   }
 });
 return Promise.all(tokensDelete);
}

部署函數

該功能僅在您部署後才處於活動狀態,要部署它,請在命令行中運行:

firebase deploy --only functions

這是您應該看到的控制台輸出:

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (X.XX KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function addWelcomeMessages...
i  functions: updating function blurOffensiveImages...
i  functions: creating function sendNotifications...
✔  functions[addWelcomeMessages]: Successful update operation.
✔  functions[blurOffensiveImages]: Successful updating operation.
✔  functions[sendNotifications]: Successful create operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview

測試功能

  1. 函數部署成功後,使用託管 URL(格式為https://<project-id>.firebaseapp.com )在瀏覽器中打開您的應用程序。
  2. 如果您是第一次登錄應用程序,請確保在出現提示時允許通知: 8b9d0c66dc36153d.png
  3. 關閉聊天應用程序選項卡或顯示其他選項卡:僅當應用程序在後台時才會顯示通知。如果您想了解如何在您的應用程序處於前台時接收消息,請查看我們的文檔
  4. 使用其他瀏覽器(或隱身窗口)登錄應用並發布消息。您應該會看到第一個瀏覽器顯示的通知: 45282ab12b28b926.png

11. 恭喜!

您已使用 Firebase SDK for Cloud Functions 並將服務器端組件添加到聊天應用程序。

我們涵蓋的內容

  • 使用 Firebase SDK for Cloud Functions 創作 Cloud Functions。
  • 根據 Auth、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
  • 將 Firebase 雲消息傳遞支持添加到您的 Web 應用程序。
  • 使用 Firebase CLI 部署 Cloud Functions。

下一步

學到更多