ขยาย Cloud Storage ด้วย Cloud Functions

คุณสามารถทริกเกอร์ฟังก์ชันเพื่อตอบสนองต่อการอัปโหลด อัปเดต หรือ ลบไฟล์และโฟลเดอร์ใน Cloud Storage ได้

ตัวอย่างในหน้านี้อิงตามฟังก์ชันตัวอย่างที่ทริกเกอร์เมื่อมีการอัปโหลดไฟล์รูปภาพ ไปยัง Cloud Storage ฟังก์ชันตัวอย่างนี้แสดง วิธีเข้าถึงแอตทริบิวต์ของเหตุการณ์ วิธีดาวน์โหลดไฟล์ไปยังอินสแตนซ์ Cloud Functions และข้อมูลพื้นฐานอื่นๆ เกี่ยวกับการจัดการเหตุการณ์ Cloud Storage

นำเข้าโมดูลที่จำเป็น

หากต้องการเริ่มต้นใช้งาน ให้นำเข้าโมดูลที่จำเป็นสำหรับการจัดการ Cloud Storage เหตุการณ์ดังนี้

Node.js

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

Python

 from firebase_functions import storage_fn

หากต้องการสร้างตัวอย่างฉบับเต็ม ให้เพิ่มทรัพยากร Dependency สำหรับ 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

ใช้รูปแบบต่อไปนี้เพื่อกำหนดขอบเขตฟังก์ชันของคุณไปยัง Bucket Cloud Storage ที่เฉพาะเจาะจงและตั้งค่าตัวเลือกที่ต้องการ

Node.js

// scope handler to a specific bucket, using storage options parameter
exports.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]):
    # ...

ในทางตรงกันข้าม ฟังก์ชันตัวสร้างภาพขนาดย่อตัวอย่างจะกำหนดขอบเขตไปยัง Bucket เริ่มต้นของโปรเจ็กต์

Node.js

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

Python

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

ตั้งค่าตำแหน่งฟังก์ชัน

ตำแหน่งที่ไม่ตรงกันอาจทำให้การติดตั้งใช้งานล้มเหลว นอกจากนี้ ระยะห่างระหว่างตำแหน่ง ของ Bucket Cloud Storageกับตำแหน่งของฟังก์ชันอาจทำให้เกิดความหน่วงแฝงของเครือข่ายอย่างมาก หากต้องการหลีกเลี่ยงสถานการณ์เหล่านี้ ให้ระบุ ตำแหน่งฟังก์ชันเพื่อให้ตรงกับตำแหน่ง Bucket/ทริกเกอร์ด้วยวิธีใดวิธีหนึ่งต่อไปนี้

  • ตำแหน่งฟังก์ชันเหมือนกับตำแหน่งทริกเกอร์
  • ตำแหน่งฟังก์ชันอยู่ภายในตำแหน่งทริกเกอร์ (เมื่อภูมิภาคทริกเกอร์เป็นภูมิภาคคู่/หลายภูมิภาค)
  • ฟังก์ชันอาจอยู่ในตำแหน่งใดก็ได้หากตั้งค่าภูมิภาคทริกเกอร์เป็น us-central1

จัดการเหตุการณ์ Cloud Storage

ตัวแฮนเดิลต่อไปนี้พร้อมให้ใช้งานสำหรับการตอบสนองต่อเหตุการณ์ Cloud Storage

Node.js

  • onObjectArchived ส่งเมื่อ Bucket ได้เปิดใช้ การกำหนดเวอร์ชันออบเจ็กต์เท่านั้น เหตุการณ์นี้แสดงให้เห็นว่าเวอร์ชันที่ทำงานอยู่ของออบเจ็กต์ได้กลายเป็นเวอร์ชันที่เก็บถาวร เนื่องจากมีการเก็บถาวรหรือเขียนทับโดยการอัปโหลดออบเจ็กต์ที่มีชื่อเดียวกัน
  • onObjectDeleted ส่งเมื่อออบเจ็กต์ถูกลบอย่างถาวร ซึ่ง รวมถึงออบเจ็กต์ที่ถูกเขียนทับหรือลบจากส่วนหนึ่งของการกำหนดค่าอายุการใช้งานของ Bucket สำหรับ Bucket ที่เปิดใช้ การกำหนดเวอร์ชันออบเจ็กต์ ระบบจะไม่ส่งเหตุการณ์นี้เมื่อมีการเก็บออบเจ็กต์ถาวร (ดู onArchive) แม้ ว่าการเก็บถาวรจะเกิดขึ้นผ่านเมธอด storage.objects.delete ก็ตาม
  • onObjectFinalized ส่งเมื่อมีการสร้างออบเจ็กต์ใหม่ (หรือรุ่นใหม่ของออบเจ็กต์ที่มีอยู่) ใน Bucket เรียบร้อยแล้ว ซึ่งรวมถึงการคัดลอกหรือเขียนออบเจ็กต์ที่มีอยู่ใหม่ การอัปโหลดที่ล้มเหลวจะไม่ทริกเกอร์เหตุการณ์นี้
  • onMetadataUpdated ส่งเมื่อมีการเปลี่ยนแปลงข้อมูลเมตาของออบเจ็กต์ที่มีอยู่

Python

  • on_object_archived ส่งเมื่อ Bucket ได้เปิดใช้ การกำหนดเวอร์ชันออบเจ็กต์เท่านั้น เหตุการณ์นี้แสดงให้เห็นว่าเวอร์ชันที่ทำงานอยู่ของออบเจ็กต์ได้กลายเป็นเวอร์ชันที่เก็บถาวร เนื่องจากมีการเก็บถาวรหรือเขียนทับโดยการอัปโหลดออบเจ็กต์ที่มีชื่อเดียวกัน
  • on_object_deleted ส่งเมื่อออบเจ็กต์ถูกลบอย่างถาวร ซึ่ง รวมถึงออบเจ็กต์ที่ถูกเขียนทับหรือลบจากส่วนหนึ่งของการกำหนดค่าอายุการใช้งานของ Bucket สำหรับ Bucket ที่เปิดใช้ การกำหนดเวอร์ชันออบเจ็กต์ ระบบจะไม่ส่งเหตุการณ์นี้เมื่อมีการเก็บออบเจ็กต์ถาวร (ดู onArchive) แม้ ว่าการเก็บถาวรจะเกิดขึ้นผ่านเมธอด storage.objects.delete ก็ตาม
  • on_object_finalized ส่งเมื่อมีการสร้างออบเจ็กต์ใหม่ (หรือรุ่นใหม่ของออบเจ็กต์ที่มีอยู่) ใน Bucket เรียบร้อยแล้ว ซึ่งรวมถึงการคัดลอกหรือเขียนออบเจ็กต์ที่มีอยู่ใหม่ การอัปโหลดที่ล้มเหลวจะไม่ทริกเกอร์เหตุการณ์นี้
  • 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 เมื่อทำงานแบบไม่พร้อมกัน โปรดตรวจสอบว่าคุณได้แสดงผล Promise ของ JavaScript ใน Callback

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