טריגרים של Cloud Storage


אתם יכולים להפעיל פונקציה בתגובה להעלאה, לעדכון או למחיקה של קבצים ותיקיות ב-Cloud Storage.

הדוגמאות בדף הזה מבוססות על פונקציה לדוגמה שמופעלת כשמעלים קובצי תמונות אל Cloud Storage. פונקציית הדוגמה הזו מדגימה איך לגשת למאפייני אירועים, איך להוריד קובץ למופע Cloud Functions ועוד פעולות בסיסיות לטיפול באירועי Cloud Storage.

ייבוא המודולים הנדרשים

כדי להתחיל, מייבאים את המודול שנדרש לטיפול באירועים של Cloud Storage:

Node.js

 const {onObjectFinalized} = require("firebase-functions/v2/storage");

Python

 from firebase_functions import storage_fn

כדי ליצור את הדוגמה המלאה, צריך להוסיף גם את התלות בכלי Firebase Admin SDK ובכלי לעיבוד תמונות:

Node.js

 const {initializeApp} = require("firebase-admin/app");
const {getStorage} = require("firebase-admin/storage");
const logger = require("firebase-functions/logger");
const path = require("path");

// library for image resizing
const sharp = require("sharp");

initializeApp();

Python

 import io
import pathlib

from PIL import Image

from firebase_admin import initialize_app

initialize_app()
from firebase_admin import storage

הגדרת היקף לפונקציה Cloud Storage

כדי להגדיר את הפונקציה לקטגוריה ספציפית של Cloud Storage ולהגדיר את האפשרויות הרצויות, משתמשים בתבנית הבאה:

Node.js

// scope handler to a specific bucket, using storage options parameter
export archivedopts = onObjectArchived({ bucket: "myBucket" }, (event) => {
  //…
});

Python

# Scope handler to a specific bucket using storage options parameter
@storage_fn.on_object_archived(bucket="myBucket")
def archived_bucket(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
    # ...

לעומת זאת, הפונקציה לדוגמה ליצירת תמונות ממוזערות מוגבלת לקטגוריית ברירת המחדל של הפרויקט:

Node.js

exports.generateThumbnail = onObjectFinalized({cpu: 2}, async (event) => {
// ...
});

Python

@storage_fn.on_object_archived()
def generatethumbnail(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
    # ...

הגדרת המיקום של הפונקציה

חוסר התאמה בין המיקומים עלול לגרום לכשל בפריסה. בנוסף, המרחק בין המיקום של קטגוריית Cloud Storage לבין המיקום של הפונקציה יכול ליצור זמן אחזור משמעותי ברשת. כדי להימנע מהמצבים האלה, צריך לציין את מיקום הפונקציה כך שיתאים למיקום של מאגר או של טריגר באחת מהדרכים הבאות:

  • מיקום הפונקציה זהה למיקום הטריגר
  • המיקום של הפונקציה נמצא בתוך המיקום של הטריגר (כשהאזור של הטריגר הוא אזור כפול או אזור מרובה)
  • הפונקציה יכולה להיות בכל מיקום אם אזור הטריגר מוגדר ל-us-central1

טיפול באירועי Cloud Storage

אלה הגורמים המטפלים הזמינים בתגובה לאירועים של Cloud Storage:

Node.js

  • onObjectArchived מתרחש רק אם האפשרות object versioning מופעלת בקטגוריה. האירוע הזה מציין שהגרסה הפעילה של אובייקט הפכה לגרסה בארכיון – בגלל שהיא הועברה לארכיון או כי העלאה של אובייקט באותו שם החליפה את האובייקט הקיים.
  • onObjectDeleted מתרחש כשאובייקט נמחק באופן סופי. זה כולל גם אובייקטים שנמחקים או שנכתבים עליהם נתונים חדשים כחלק מהגדרות מחזור החיים של הקטגוריה. בקטגוריות שבהן מופעלת האפשרות object versioning, האירוע הזה לא נשלח כשמעבירים אובייקט לארכיון (ראו onArchive), גם אם ההעברה לארכיון מתבצעת באמצעות השיטה storage.objects.delete.
  • onObjectFinalized מתרחש כשנוצר אובייקט חדש בקטגוריה (או כשאובייקט קיים נוצר מחדש). זה כולל העתקה או שכתוב של אובייקט קיים. האירוע הזה לא יופעל במקרה של העלאה שנכשלה.
  • onMetadataUpdated מתרחש כשהמטא-נתונים של אובייקט קיים משתנים.

Python

  • on_object_archived מתרחש רק אם האפשרות object versioning מופעלת בקטגוריה. האירוע הזה מציין שהגרסה הפעילה של אובייקט הפכה לגרסה בארכיון – בגלל שהיא הועברה לארכיון או כי העלאה של אובייקט באותו שם החליפה את האובייקט הקיים.
  • on_object_deleted מתרחש כשאובייקט נמחק באופן סופי. זה כולל גם אובייקטים שנמחקים או שנכתבים עליהם נתונים חדשים כחלק מהגדרות מחזור החיים של הקטגוריה. בקטגוריות שבהן מופעלת האפשרות object versioning, האירוע הזה לא נשלח כשמעבירים אובייקט לארכיון (ראו onArchive), גם אם ההעברה לארכיון מתבצעת באמצעות השיטה storage.objects.delete.
  • on_object_finalized מתרחש כשנוצר אובייקט חדש בקטגוריה (או כשאובייקט קיים נוצר מחדש). זה כולל העתקה או שכתוב של אובייקט קיים. האירוע הזה לא יופעל במקרה של העלאה שנכשלה.
  • on_metadata_updated מתרחש כשהמטא-נתונים של אובייקט קיים משתנים.

גישה למאפייני אובייקט Cloud Storage

Cloud Functions חושף מספר Cloud Storage מאפייני אובייקט, כמו הגודל וסוג התוכן של הקובץ שעודכן. הערך של המאפיין metageneration גדל בכל פעם שחל שינוי במטא-נתונים של האובייקט. באובייקטים חדשים, הערך של metageneration הוא 1.

Node.js

const fileBucket = event.data.bucket; // Storage bucket containing the file.
const filePath = event.data.name; // File path in the bucket.
const contentType = event.data.contentType; // File content type.

Python

bucket_name = event.data.bucket
file_path = pathlib.PurePath(event.data.name)
content_type = event.data.content_type

בדוגמה ליצירת תמונות ממוזערות נעשה שימוש בחלק מהמאפיינים האלה כדי לזהות מקרים שבהם הפונקציה מחזירה:

Node.js

// Exit if this is triggered on a file that is not an image.
if (!contentType.startsWith("image/")) {
  return logger.log("This is not an image.");
}
// Exit if the image is already a thumbnail.
const fileName = path.basename(filePath);
if (fileName.startsWith("thumb_")) {
  return logger.log("Already a Thumbnail.");
}

Python

# Exit if this is triggered on a file that is not an image.
if not content_type or not content_type.startswith("image/"):
    print(f"This is not an image. ({content_type})")
    return

# Exit if the image is already a thumbnail.
if file_path.name.startswith("thumb_"):
    print("Already a thumbnail.")
    return

הורדה, שינוי והעלאה של קובץ

במקרים מסוימים, יכול להיות שלא יהיה צורך להוריד קבצים מ-Cloud Storage. עם זאת, כדי לבצע משימות אינטנסיביות כמו יצירת תמונה ממוזערת מקובץ שמאוחסן ב-Cloud Storage, צריך להוריד קבצים למופע של הפונקציות – כלומר, למכונה הווירטואלית שמריצה את הקוד.

אפשר להשתמש ב-Cloud Functions יחד עם תוכנות לעיבוד תמונות כמו sharp ל-Node.js ו-Pillow ל-Python, כדי לבצע מניפולציות בקובצי תמונות גרפיות. הדוגמה הבאה מראה איך ליצור תמונה ממוזערת לקובץ תמונה שהועלה:

Node.js

/**
 * When an image is uploaded in the Storage bucket,
 * generate a thumbnail automatically using sharp.
 */
exports.generateThumbnail = onObjectFinalized({cpu: 2}, async (event) => {

  const fileBucket = event.data.bucket; // Storage bucket containing the file.
  const filePath = event.data.name; // File path in the bucket.
  const contentType = event.data.contentType; // File content type.

  // Exit if this is triggered on a file that is not an image.
  if (!contentType.startsWith("image/")) {
    return logger.log("This is not an image.");
  }
  // Exit if the image is already a thumbnail.
  const fileName = path.basename(filePath);
  if (fileName.startsWith("thumb_")) {
    return logger.log("Already a Thumbnail.");
  }

  // Download file into memory from bucket.
  const bucket = getStorage().bucket(fileBucket);
  const downloadResponse = await bucket.file(filePath).download();
  const imageBuffer = downloadResponse[0];
  logger.log("Image downloaded!");

  // Generate a thumbnail using sharp.
  const thumbnailBuffer = await sharp(imageBuffer).resize({
    width: 200,
    height: 200,
    withoutEnlargement: true,
  }).toBuffer();
  logger.log("Thumbnail created");

  // Prefix 'thumb_' to file name.
  const thumbFileName = `thumb_${fileName}`;
  const thumbFilePath = path.join(path.dirname(filePath), thumbFileName);

  // Upload the thumbnail.
  const metadata = {contentType: contentType};
  await bucket.file(thumbFilePath).save(thumbnailBuffer, {
    metadata: metadata,
  });
  return logger.log("Thumbnail uploaded!");
});

מורידים את הקובץ לספרייה זמנית במופע Cloud Functions. במיקום הזה, תוכלו לעבד את הקובץ לפי הצורך ואז להעלות אותו אל Cloud Storage. כשמבצעים משימות אסינכרוניות, חשוב להחזיר הבטחה של JavaScript בקריאה החוזרת.

Python

@storage_fn.on_object_finalized()
def generatethumbnail(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):
    """When an image is uploaded in the Storage bucket, generate a thumbnail
    automatically using Pillow."""

    bucket_name = event.data.bucket
    file_path = pathlib.PurePath(event.data.name)
    content_type = event.data.content_type

    # Exit if this is triggered on a file that is not an image.
    if not content_type or not content_type.startswith("image/"):
        print(f"This is not an image. ({content_type})")
        return

    # Exit if the image is already a thumbnail.
    if file_path.name.startswith("thumb_"):
        print("Already a thumbnail.")
        return

    bucket = storage.bucket(bucket_name)

    image_blob = bucket.blob(str(file_path))
    image_bytes = image_blob.download_as_bytes()
    image = Image.open(io.BytesIO(image_bytes))

    image.thumbnail((200, 200))
    thumbnail_io = io.BytesIO()
    image.save(thumbnail_io, format="png")
    thumbnail_path = file_path.parent / pathlib.PurePath(f"thumb_{file_path.stem}.png")
    thumbnail_blob = bucket.blob(str(thumbnail_path))
    thumbnail_blob.upload_from_string(thumbnail_io.getvalue(), content_type="image/png")

הקוד הזה יוצר תמונה ממוזערת בגודל 200x200 לתמונה שנשמרה בספרייה זמנית, ואז מעלה אותה בחזרה אל Cloud Storage.