1. 總覽
在本程式碼研究室中,您將瞭解如何使用 Cloud Functions for Firebase 為即時通訊網頁應用程式新增功能,方法是向該應用程式的使用者傳送通知。
課程內容
- 使用 Firebase SDK 建立 Google Cloud Functions。
- 根據 Auth、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
- 在網頁應用程式中加入 Firebase 雲端通訊支援功能。
事前準備
2. 取得程式碼範例
從指令列複製 GitHub 存放區:
git clone https://github.com/firebase/friendlychat
匯入範例應用程式
使用 IDE 開啟或匯入範例程式碼目錄中的 cloud-functions-start
目錄。這個目錄包含程式碼研究室的起始程式碼,其中包含完整運作的 Chat Web 應用程式。
3. 建立 Firebase 專案並設定應用程式
建立專案
在 Firebase 控制台中,按一下「新增專案」,並將專案命名為「FriendlyChat」。
按一下 [Create Project] (建立專案)。
升級至 Blaze 方案
如要使用 Cloud Functions for Firebase 和 Cloud Storage for Firebase,您的 Firebase 專案必須採用即付即用 (Blaze) 定價方案,也就是說,專案必須連結至 Cloud Billing 帳戶。
- 您必須提供付款方式 (例如信用卡),才能建立 Cloud Billing 帳戶。
- 如果您是 Firebase 和 Google Cloud 的新手,請確認您是否符合$300 美元的抵免額和免費試用 Cloud Billing 帳戶的資格。
- 如果您是為了參加活動而進行這個程式碼研究室,請向活動主辦單位詢問是否有任何 Cloud 抵用金。
如果您沒有信用卡,或是不想繼續使用 Blaze 定價方案,建議您使用 Firebase 模擬器套件,在本機免費模擬 Cloud Functions。
所有 Firebase 專案 (包括採用 Blaze 定價方案的專案) 仍可使用 Cloud Functions 的免付費使用量配額。本程式碼研究室中的步驟不會超過免費方案的用量限制。不過,您可能會看到 Cloud Storage 收取的少許費用 (約 $0.03 美元),用於代管 Cloud Functions 建構映像檔。
如要將專案升級至 Blaze 方案,請按照下列步驟操作:
- 在 Firebase 控制台中,選取「升級方案」。
- 選取 Blaze 方案。按照畫面上的指示將 Cloud Billing 帳戶連結至專案。
如果您需要在升級過程中建立 Cloud Billing 帳戶,可能需要返回 Firebase 控制台的升級流程,才能完成升級。
啟用 Google 驗證
為了讓使用者登入應用程式,我們會使用 Google 驗證機制,但需要先啟用。
在 Firebase 主控台中,依序開啟「Build」部分 >「Authentication」 >「Sign-in method」分頁標籤 (或按這裡前往該分頁)。接著啟用 Google 登入供應器,然後按一下「儲存」。這樣一來,使用者就能透過 Google 帳戶登入網頁應用程式。
您也可以將應用程式的公開名稱設為「Friendly Chat」:
設定 Cloud Storage for Firebase
應用程式會使用 Cloud Storage 上傳圖片。
以下說明如何在 Firebase 專案中設定 Cloud Storage for Firebase:
- 在 Firebase 主控台的左側面板中,展開「Build」,然後選取「Storage」。
- 按一下「開始使用」。
- 選取預設儲存體值區的位置。
US-WEST1
、US-CENTRAL1
和US-EAST1
中的值區可使用 Google Cloud Storage 的「永遠免費」方案。其他所有位置的值區都會遵循 Google Cloud Storage 的定價和用量。 - 按一下「以測試模式啟動」。請詳閱安全性規則免責事項。
請勿在未為儲存體儲存桶新增安全性規則的情況下,發布或公開應用程式。 - 按一下「建立」。
新增網路應用程式
在 Firebase 主控台上新增網頁應用程式。如要新增,請前往「專案設定」,然後向下捲動至「新增應用程式」。選擇「網頁」做為平台,並勾選設定 Firebase Hosting 的方塊,接著註冊應用程式,然後按一下「下一步」,完成後續步驟,最後按一下「繼續前往控制台」。
4. 安裝 Firebase 指令列介面
您可以使用 Firebase 指令列介面 (CLI) 在本機上提供網頁應用程式,並部署網頁應用程式和 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. 部署及執行網頁應用程式
您已匯入及設定專案,現在可以首次執行網路應用程式了!開啟終端機視窗,前往 cloud-functions-start
資料夾,然後使用以下指令將網頁應用程式部署至 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
開啟網頁應用程式
最後一行應顯示代管網址。網頁應用程式現在應透過這個網址提供服務,網址格式應為 https://<project-id>.firebaseapp.com。請開啟這個網址。您應該會看到聊天應用程式的正常運作使用者介面。
使用「使用 Google 帳戶登入」按鈕登入應用程式,然後隨意新增訊息和圖片:
如果您在新的瀏覽器上首次登入應用程式,請務必在系統提示時允許接收通知:
我們稍後會啟用通知功能。
如果不小心按下「封鎖」,你可以在 Chrome 全方位工具列中點選網址左側的「安全」🔒?按鈕,然後切換「通知」旁的列:
接下來,我們將使用 Cloud Functions 專用的 Firebase SDK 新增一些功能。
6. 函式目錄
您可以使用 Cloud Functions 輕鬆在雲端執行程式碼,不必設定伺服器。我們將逐步說明如何建構函式,以回應 Firebase Auth、Cloud Storage 和 Firebase Firestore 資料庫事件。我們先從驗證開始。
使用 Cloud Functions 專用的 Firebase SDK 時,函式程式碼會位於 functions
目錄下方 (預設)。您的 Functions 程式碼也是 Node.js 應用程式,因此需要 package.json
,提供應用程式相關資訊並列出依附元件。
為方便您操作,我們已建立 functions/index.js
檔案,用於儲存程式碼。您可以先查看這個檔案再繼續操作。
cd functions
ls
如果您不熟悉 Node.js,建議先進一步瞭解這項技術,再繼續進行程式碼研究室。
package.json
檔案已列出兩個必要的依附元件:Cloud Functions 專用的 Firebase SDK 和 Firebase 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 管理員模組
本程式碼研究室會需要兩個模組: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.
部署至 Cloud Functions 環境或其他 Google Cloud Platform 容器時,系統會自動設定 Firebase Admin SDK,而這會在我們呼叫 admin.initializeApp()
時發生,且沒有任何引數。
接下來,我們來新增一個函式,在使用者首次登入即時通訊應用程式時執行,並新增即時通訊訊息來歡迎使用者。
8. 歡迎新使用者
即時通訊訊息結構
發布至 FriendlyChat 即時通訊動態消息會儲存在 Cloud Firestore 中。讓我們來看看我們用於訊息的資料結構。如要進行這項操作,請在聊天室中發布「Hello World」的新訊息:
應該會顯示如下:
在 Firebase 控制台中,按一下「Build」專區下方的「Firestore Database」。您應該會看到訊息集合和一個包含您撰寫的訊息的文件:
如您所見,聊天訊息會以文件形式儲存在 Cloud Firestore 中,其中的 name
、profilePicUrl
、text
和 timestamp
屬性會新增至 messages
集合。
新增歡迎訊息
第一個 Cloud 函式會新增訊息,歡迎新使用者加入即時通訊。為此,我們可以使用觸發事件 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
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
測試函式
函式成功部署後,您必須讓使用者首次登入。
- 使用代管網址 (以
https://<project-id>.firebaseapp.com
格式) 在瀏覽器中開啟應用程式。 - 使用新使用者,透過「Sign In」按鈕首次登入應用程式。
- 如果您已登入應用程式,可以開啟 Firebase 控制台驗證,然後從使用者清單中刪除您的帳戶。然後重新登入。
- 登入後,系統會自動顯示歡迎訊息:
9. 圖片審核
使用者可以在聊天室中上傳所有類型的圖片,但請務必審核冒犯性圖片,尤其是在公開的社群平台上。在 FriendlyChat 中,發布至聊天的圖片會儲存在 Cloud Storage 值區中。
您可以使用 Cloud Functions 搭配 functions.storage().onFinalize
觸發事件,偵測新的圖片上傳作業。每次在 Cloud Storage 中上傳或修改新檔案時,系統都會執行這項操作。
我們會按照下列程序審核圖片:
- 使用 Cloud Vision API 檢查圖片是否標示為成人或暴力內容。
- 如果圖片已標記,請在執行中的 Functions 例項上下載該圖片。
- 使用 ImageMagick 模糊處理圖片。
- 將模糊處理過的圖片上傳至 Cloud Storage。
啟用 Cloud Vision API
由於我們會在這個函式中使用 Google Cloud Vision API,因此您必須在 Firebase 專案中啟用該 API。請按照這個連結操作,選取 Firebase 專案並啟用 API:
安裝依附元件
為了審核圖片,我們會使用 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 核心模組 (path
、os
和 fs
),請將以下行新增至 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
測試函式
函式成功部署後:
- 使用代管網址 (以
https://<project-id>.firebaseapp.com
格式) 在瀏覽器中開啟應用程式。 - 登入應用程式後,上傳圖片:
- 請選擇最具攻擊性的圖片上傳 (也可以使用這張食人殭屍圖片),幾秒後,貼文就會重新整理,並顯示經過模糊處理的圖片:
10. 新訊息通知
在本節中,您將新增 Cloud 函式,在有人發布新訊息時,向即時通訊參與者傳送通知。
您可以使用 Firebase 雲端通訊 (FCM),向跨平台使用者傳送可靠的通知。如要向使用者傳送通知,您需要使用者的 FCM 裝置權杖。使用中的即時通訊網頁應用程式,會在使用者透過新瀏覽器或裝置首次開啟應用程式時,收集使用者的裝置權杖。這些符記會儲存在 fcmTokens
集合的 Cloud Firestore 中。
如要瞭解如何在網路應用程式上取得 FCM 裝置符記,請參閱 Firebase Web Codelab。
傳送通知
如要偵測新訊息發布時間,您將使用 functions.firestore.document().onCreate
Cloud 函式觸發條件,在 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
測試函式
- 函式部署完成後,請使用代管網址 (以
https://<project-id>.firebaseapp.com
格式) 在瀏覽器中開啟應用程式。 - 如果您是初次登入應用程式,請務必在系統提示時允許接收通知:
- 關閉 Chat 應用程式分頁或顯示其他分頁:只有在應用程式處於背景執行時,系統才會顯示通知。如要瞭解如何在應用程式處於前景時接收訊息,請參閱我們的說明文件。
- 使用其他瀏覽器 (或無痕模式視窗) 登入應用程式,然後發布訊息。您應該會看到第一個瀏覽器顯示的通知:
11. 恭喜!
您已使用 Cloud Functions 專用的 Firebase SDK,並在即時通訊應用程式中新增伺服器端元件。
涵蓋內容
- 使用 Cloud Functions 專用的 Firebase SDK 編寫 Cloud Functions。
- 根據 Auth、Cloud Storage 和 Cloud Firestore 事件觸發 Cloud Functions。
- 在網頁應用程式中加入 Firebase 雲端通訊支援功能。
- 使用 Firebase CLI 部署 Cloud Functions。
後續步驟
- 瞭解其他 Cloud Functions 觸發器類型。
- 在自家應用程式中使用 Firebase 和 Cloud Functions。