عوامل التشغيل في 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

لإنشاء النموذج الكامل، أضِف أيضًا التبعيات لحزمة تطوير البرامج (SDK) الخاصة بمشرف Firebase وأدوات معالجة الصور:

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 لا يتم إرساله إلا عند تفعيل الحزمة لإصدار العناصر. يشير هذا الحدث إلى أن النسخة المباشرة من الكائن أصبح نسخة مؤرشفة من العنصر، إما لأنه تمت أرشفته أو لأنه تم استبداله من خلال تحميل عنصر يحمل الاسم نفسه.
  • onObjectDeleted يتم إرساله عند حذف عنصر نهائيًا. ويتضمّن ذلك العناصر التي يتم استبدالها أو يتم حذفها كجزء من إعداد دورة حياة الحزمة. بالنسبة إلى الحِزم التي تم تفعيل إصدار الكائنات فيها، لا يتم إرسال ذلك عند أرشفة عنصر (راجِع onArchive)، حتى في حال الأرشفة من خلال طريقة storage.objects.delete.
  • onObjectFinalized يتم إرساله عند إنشاء كائن جديد (أو جيل جديد من كائن حالي) في الحزمة بنجاح. ويشمل ذلك نسخ عنصر موجود أو إعادة كتابته. ولا يؤدي تعذُّر التحميل إلى بدء هذا الحدث.
  • onMetadataUpdated يتم إرساله عند تغيير البيانات الوصفية لعنصر موجود.

Python

  • on_object_archived لا يتم إرساله إلا عند تفعيل الحزمة لإصدار العناصر. يشير هذا الحدث إلى أن النسخة المباشرة من الكائن أصبح نسخة مؤرشفة من العنصر، إما لأنه تمت أرشفته أو لأنه تم استبداله من خلال تحميل عنصر يحمل الاسم نفسه.
  • on_object_deleted يتم إرساله عند حذف عنصر نهائيًا. ويتضمّن ذلك العناصر التي يتم استبدالها أو يتم حذفها كجزء من إعداد دورة حياة الحزمة. بالنسبة إلى الحِزم التي تم تفعيل إصدار الكائنات فيها، لا يتم إرسال ذلك عند أرشفة عنصر (راجِع onArchive)، حتى في حال الأرشفة من خلال طريقة storage.objects.delete.
  • on_object_finalized يتم إرساله عند إنشاء كائن جديد (أو جيل جديد من كائن حالي) في الحزمة بنجاح. ويشمل ذلك نسخ عنصر موجود أو إعادة كتابته. ولا يؤدي تعذُّر التحميل إلى بدء هذا الحدث.
  • on_metadata_updated يتم إرساله عند تغيير البيانات الوصفية لعنصر موجود.

الوصول إلى سمات عناصر Cloud Storage

تعرض دوال Cloud عددًا من سمات عناصر 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 مع برامج معالجة الصور مثل 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.