Rozszerzanie Cloud Storage za pomocą Cloud Functions

Możesz uruchomić funkcję w odpowiedzi na przesyłanie, aktualizowanie lub usuwanie plików i folderów w Cloud Storage.

Przykłady na tej stronie są oparte na przykładowej funkcji, która uruchamia się, gdy pliki graficzne są przesyłane do Cloud Storage. Ta przykładowa funkcja pokazuje jak uzyskać dostęp do atrybutów zdarzenia, jak pobrać plik do instancji Cloud Functions oraz inne podstawowe informacje o obsłudze zdarzeń Cloud Storage.

Importowanie wymaganych modułów

Na początek zaimportuj moduł wymagany do obsługi Cloud Storage zdarzeń:

Node.js

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

Python

 from firebase_functions import storage_fn

Aby utworzyć pełny przykład, dodaj też zależności dla pakietu Firebase Admin SDK i narzędzi do przetwarzania obrazów:

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

Określanie zakresu funkcji Cloud Storage

Aby określić zakres funkcji do konkretnego zasobnika Cloud Storage i ustawić dowolne opcje, użyj tego wzorca:

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

Natomiast przykładowa funkcja generowania miniatur jest ograniczona do domyślnego zasobnika projektu:

Node.js

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

Python

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

Ustawianie lokalizacji funkcji

Niezgodność lokalizacji może spowodować niepowodzenie wdrożenia. Odległość między lokalizacją zasobnika Cloud Storage a lokalizacją funkcji może też powodować znaczne opóźnienia w sieci. Aby uniknąć tych sytuacji, określ lokalizację funkcji tak, aby pasowała do lokalizacji zasobnika lub aktywatora w jeden z tych sposobów:

  • Lokalizacja funkcji jest taka sama jak lokalizacja aktywatora.
  • Lokalizacja funkcji znajduje się w lokalizacji aktywatora (gdy region aktywatora jest regionem podwójnym lub wieloregionowym).
  • Funkcja może znajdować się w dowolnej lokalizacji, jeśli region aktywatora jest ustawiony na us-central1.

Obsługa zdarzeń Cloud Storage

Dostępne są te moduły obsługi zdarzeń Cloud Storage:

Node.js

  • onObjectArchived Wysyłane tylko wtedy, gdy w zasobniku jest włączona obsługa wersji obiektów . To zdarzenie wskazuje, że bieżąca wersja obiektu stała się wersją archiwalną – została zarchiwizowana albo zastąpiona przez przesłany obiekt o tej samej nazwie.
  • onObjectDeleted Wysyłane po nieodwracalnym usunięciu obiektu. Dotyczy to obiektów, które zostały zastąpione lub usunięte w ramach konfiguracji cyklu życia zasobnika. W przypadku zasobników z włączoną obsługą wersji obiektów nie jest wysyłane, gdy obiekt zostanie zarchiwizowany (patrz onArchive), nawet jeśli archiwizacja nastąpi za pomocą metody storage.objects.delete.
  • onObjectFinalized Wysyłane po utworzeniu w zasobniku nowego obiektu (lub nowej generacji istniejącego obiektu). Obejmuje to skopiowanie lub ponowne zapisanie istniejącego obiektu. Zdarzenia tego nie aktywuje nieudane przesyłanie.
  • onMetadataUpdated Wysyłane po zmianie metadanych istniejącego obiektu.

Python

  • on_object_archived Wysyłane tylko wtedy, gdy w zasobniku jest włączona obsługa wersji obiektów . To zdarzenie wskazuje, że bieżąca wersja obiektu stała się wersją archiwalną – została zarchiwizowana albo zastąpiona przez przesłany obiekt o tej samej nazwie.
  • on_object_deleted Wysyłane po nieodwracalnym usunięciu obiektu. Dotyczy to obiektów, które zostały zastąpione lub usunięte w ramach konfiguracji cyklu życia zasobnika. W przypadku zasobników z włączoną obsługą wersji obiektów nie jest wysyłane, gdy obiekt zostanie zarchiwizowany (patrz onArchive), nawet jeśli archiwizacja nastąpi za pomocą metody storage.objects.delete.
  • on_object_finalized Wysyłane po utworzeniu w zasobniku nowego obiektu (lub nowej generacji istniejącego obiektu). Obejmuje to skopiowanie lub ponowne zapisanie istniejącego obiektu. Zdarzenia tego nie aktywuje nieudane przesyłanie.
  • on_metadata_updated Wysyłane po zmianie metadanych istniejącego obiektu.

Uzyskiwanie dostępu do atrybutów obiektu Cloud Storage

Cloud Functions udostępnia wiele atrybutów obiektu Cloud Storage takich jak rozmiar obiektu i typ treści zaktualizowanego pliku. Atrybut metageneration jest zwiększany za każdym razem, gdy zmieniają się metadane obiektu. W przypadku nowych obiektów wartość metageneration wynosi 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

Przykładowy kod generowania miniatur używa niektórych z tych atrybutów do wykrywania przypadków, w których funkcja zwraca wartość:

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

Pobieranie, przekształcanie i przesyłanie pliku

W niektórych przypadkach pobieranie plików z Cloud Storage może nie być konieczne. Aby jednak wykonywać złożone zadania, takie jak generowanie miniatury z pliku przechowywanego w Cloud Storage, musisz pobrać pliki do instancji funkcji, czyli maszyny wirtualnej, na której działa Twój kod.

Korzystając z Cloud Functions razem z programami do przetwarzania obrazów, takimi jak sharp w Node.js i Pillow w Pythonie, możesz manipulować plikami graficznymi. Oto przykład tworzenia miniatury przesłanego pliku graficznego:

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

Pobierz plik do tymczasowego katalogu w instancji Cloud Functions. W tej lokalizacji możesz przetworzyć plik w razie potrzeby, a następnie przesłać go do Cloud Storage. Podczas wykonywania zadań asynchronicznych pamiętaj, aby w wywołaniu zwrotnym zwrócić obietnicę 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")

Ten kod tworzy miniaturę 200 x 200 pikseli obrazu zapisanego w katalogu tymczasowym, a następnie przesyła ją z powrotem do Cloud Storage.