Cloud Storage mit Cloud Functions erweitern

Sie können eine Funktion auslösen, wenn Dateien und Ordner in Cloud Storage hochgeladen, aktualisiert oder gelöscht werden.

Die Beispiele auf dieser Seite basieren auf einer Beispielfunktion, die ausgelöst wird, wenn Bild dateien in Cloud Storage hochgeladen werden. Diese Beispielfunktion zeigt wie Sie auf Ereignisattribute zugreifen, eine Datei in eine Cloud Functions Instanz herunterladen und andere Grundlagen der Verarbeitung von Cloud Storage Ereignissen nutzen.

Erforderliche Module importieren

Importieren Sie zuerst das Modul, das für die Verarbeitung von Cloud Storage Ereignissen erforderlich ist:

Node.js

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

Python

 from firebase_functions import storage_fn

Fügen Sie die Abhängigkeiten für das Firebase Admin SDK und Bildverarbeitungstools hinzu, um das vollständige Beispiel zu erstellen:

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-Funktion eingrenzen

Verwenden Sie das folgende Muster, um Ihre Funktion auf einen bestimmten Cloud Storage Bucket zu beschränken und die gewünschten Optionen festzulegen:

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]):
    # ...

Im Gegensatz dazu ist die Beispielfunktion für die Miniaturansicht auf den Standard-Bucket für das Projekt beschränkt:

Node.js

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

Python

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

Funktionsstandort festlegen

Wenn die Standorte nicht übereinstimmen, kann die Bereitstellung fehlschlagen. Außerdem kann die Entfernung zwischen dem Standort eines Cloud Storage Buckets und dem Standort der Funktion zu einer erheblichen Netzwerklatenz führen. Um diese Situationen zu vermeiden, geben Sie den Funktionsstandort so an, dass er auf eine der folgenden Arten mit dem Bucket-/Triggerstandort übereinstimmt:

  • Der Funktionsstandort ist derselbe wie der Triggerstandort.
  • Der Funktionsstandort befindet sich innerhalb des Triggerstandorts (wenn die Triggerregion eine duale oder multiregionale Region ist).
  • Die Funktion kann sich an einem beliebigen Standort befinden, wenn die Triggerregion auf us-central1 festgelegt ist.

Cloud Storage-Ereignisse verarbeiten

Diese Handler sind für die Reaktion auf Cloud Storage-Ereignisse verfügbar:

Node.js

  • onObjectArchived: Wird nur gesendet, wenn für einen Bucket die Objektversionsverwaltung aktiviert ist. Dieses Ereignis bedeutet, dass die Live-Version eines Objekts zu einer archivierten Version geworden ist, weil sie entweder archiviert oder weil sie durch den Upload eines Objekts mit dem gleichen Namen überschrieben wurde.
  • onObjectDeleted: Wird gesendet, wenn ein Objekt endgültig gelöscht wurde. Dies gilt auch für Objekte, die im Rahmen der Lebenszykluskonfiguration des Buckets überschrieben oder gelöscht werden. Für Buckets mit aktivierter Objektversionsverwaltung wird dieses Ereignis nicht gesendet, wenn ein Objekt archiviert wird (siehe onArchive). Dies gilt auch, wenn die Archivierung über die Methode storage.objects.delete erfolgt.
  • onObjectFinalized Wird gesendet, wenn ein neues Objekt (oder eine neue Generation eines vorhandenen Objekts) im Bucket erfolgreich erstellt wurde. Dazu gehört auch das Kopieren oder Neuschreiben eines bestehenden Objekts. Ein fehlgeschlagener Upload löst dieses Ereignis nicht aus.
  • onMetadataUpdated: Wird gesendet, wenn sich die Metadaten eines vorhandenen Objekts ändern.

Python

  • on_object_archived: Wird nur gesendet, wenn für einen Bucket die Objektversionsverwaltung aktiviert ist. Dieses Ereignis bedeutet, dass die Live-Version eines Objekts zu einer archivierten Version geworden ist, weil sie entweder archiviert oder weil sie durch den Upload eines Objekts mit dem gleichen Namen überschrieben wurde.
  • on_object_deleted: Wird gesendet, wenn ein Objekt endgültig gelöscht wurde. Dies gilt auch für Objekte, die im Rahmen der Lebenszykluskonfiguration des Buckets überschrieben oder gelöscht werden. Für Buckets mit aktivierter Objektversionsverwaltung wird dieses Ereignis nicht gesendet, wenn ein Objekt archiviert wird (siehe onArchive). Dies gilt auch, wenn die Archivierung über die Methode storage.objects.delete erfolgt.
  • on_object_finalized Wird gesendet, wenn ein neues Objekt (oder eine neue Generation eines vorhandenen Objekts) im Bucket erfolgreich erstellt wurde. Dazu gehört auch das Kopieren oder Neuschreiben eines bestehenden Objekts. Ein fehlgeschlagener Upload löst dieses Ereignis nicht aus.
  • on_metadata_updated: Wird gesendet, wenn sich die Metadaten eines vorhandenen Objekts ändern.

Auf Cloud Storage Objektattribute zugreifen

Cloud Functions stellt eine Reihe von Cloud Storage Objektattributen zur Verfügung z. B. die Größe und den Inhaltstyp des aktualisierten Objekts. Das metageneration Attribut wird immer dann erhöht, wenn sich die Metadaten des Objekts ändern. Bei neuen Objekten ist der metageneration Wert gleich 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

Im Beispiel zur Miniaturansicht werden einige dieser Attribute verwendet, um Exit Fälle zu erkennen, in denen die Funktion zurückgegeben wird:

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

Datei herunterladen, transformieren und hochladen

In einigen Fällen ist es möglicherweise nicht erforderlich, Dateien aus Cloud Storage herunterzuladen. Wenn Sie jedoch intensive Aufgaben wie das Generieren einer Miniaturansicht aus einer in Cloud Storage gespeicherten Datei ausführen möchten, müssen Sie Dateien in die Funktionsinstanz herunterladen, d. h. in die virtuelle Maschine, auf der Ihr Code ausgeführt wird.

Mit Cloud Functions und Bildverarbeitungsprogrammen wie sharp für Node.js und Pillow für Python können Sie grafische Bilddateien bearbeiten. Im Folgenden finden Sie ein Beispiel für das Erstellen einer Miniaturansicht für eine hochgeladene Bilddatei:

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!");
});

Laden Sie die Datei in ein temporäres Verzeichnis in Ihrer Cloud Functions Instanz herunter. Dort können Sie die Datei nach Bedarf verarbeiten und dann in Cloud Storage hochladen. Wenn Sie asynchrone Aufgaben ausführen, müssen Sie in Ihrem Callback ein JavaScript-Promise zurückgeben.

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")

Dieser Code erstellt eine 200 × 200-Miniaturansicht für das Bild, das in einem temporären Verzeichnis gespeichert ist, und lädt es wieder in Cloud Storagehoch.