Activadores de almacenamiento en la nube


Puede activar una función en respuesta a la carga, actualización o eliminación de archivos y carpetas en Cloud Storage.

Los ejemplos de esta página se basan en una función de muestra que se activa cuando se cargan archivos de imagen en Cloud Storage. Esta función de muestra demuestra cómo acceder a los atributos de eventos, cómo descargar un archivo a una instancia de Cloud Functions y otros conceptos básicos del manejo de eventos de Cloud Storage.

Importar los módulos requeridos

Para comenzar, importe el módulo necesario para manejar eventos de Cloud Storage:

Nodo.js

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

Pitón

 from firebase_functions import storage_fn

Para crear la muestra completa, agregue también las dependencias para el SDK de Firebase Admin y las herramientas de procesamiento de imágenes:

Nodo.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();

Pitón

 import io
import pathlib

from PIL import Image

from firebase_admin import initialize_app

initialize_app()
from firebase_admin import storage

Alcance de una función de almacenamiento en la nube

Utilice el siguiente patrón para limitar su función a un depósito de Cloud Storage específico y configurar las opciones que desee:

Nodo.js

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

Pitón

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

Por el contrario, la función de generación de miniaturas de ejemplo tiene como ámbito el depósito predeterminado del proyecto:

Nodo.js

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

Pitón

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

Establecer la ubicación de la función

Una discrepancia entre ubicaciones puede provocar un error en la implementación. Además, la distancia entre la ubicación de un depósito de Cloud Storage y la ubicación de la función puede crear una latencia de red significativa. Para evitar estas situaciones, especifique la ubicación de la función para que coincida con la ubicación del depósito/activador de una de estas maneras:

  • La ubicación de la función es la misma que la ubicación del disparador.
  • La ubicación de la función está dentro de la ubicación del disparador (cuando la región del disparador es dual/multiregión)
  • La función puede estar en cualquier ubicación si la región de activación está configurada en us-central1

Manejar eventos de almacenamiento en la nube

Estos controladores para responder a eventos de Cloud Storage están disponibles:

Nodo.js

  • onObjectArchived Solo se envía cuando un depósito ha habilitado el control de versiones de objetos . Este evento indica que la versión en vivo de un objeto se ha convertido en una versión archivada, ya sea porque fue archivada o porque fue sobrescrita por la carga de un objeto con el mismo nombre.
  • onObjectDeleted Se envía cuando un objeto se ha eliminado permanentemente. Esto incluye objetos que se sobrescriben o se eliminan como parte de la configuración del ciclo de vida del depósito. Para los depósitos con control de versiones de objetos habilitado, esto no se envía cuando se archiva un objeto (consulte onArchive ), incluso si el archivo se realiza mediante el método storage.objects.delete .
  • onObjectFinalized Se envía cuando un nuevo objeto (o una nueva generación de un objeto existente) se crea correctamente en el depósito. Esto incluye copiar o reescribir un objeto existente. Una carga fallida no desencadena este evento.
  • onMetadataUpdated Se envía cuando cambian los metadatos de un objeto existente.

Pitón

  • on_object_archived Solo se envía cuando un depósito ha habilitado el control de versiones de objetos . Este evento indica que la versión en vivo de un objeto se ha convertido en una versión archivada, ya sea porque fue archivada o porque fue sobrescrita por la carga de un objeto con el mismo nombre.
  • on_object_deleted Se envía cuando un objeto se ha eliminado permanentemente. Esto incluye objetos que se sobrescriben o se eliminan como parte de la configuración del ciclo de vida del depósito. Para los depósitos con control de versiones de objetos habilitado, esto no se envía cuando se archiva un objeto (consulte onArchive ), incluso si el archivo se realiza mediante el método storage.objects.delete .
  • on_object_finalized Se envía cuando se crea exitosamente un nuevo objeto (o una nueva generación de un objeto existente) en el depósito. Esto incluye copiar o reescribir un objeto existente. Una carga fallida no desencadena este evento.
  • on_metadata_updated Se envía cuando cambian los metadatos de un objeto existente.

Acceder a los atributos de los objetos de Cloud Storage

Cloud Functions expone una serie de atributos de objetos de Cloud Storage, como el tamaño del objeto y el tipo de contenido del archivo actualizado. El atributo metageneration se incrementa cada vez que hay un cambio en los metadatos del objeto. Para objetos nuevos, el valor metageneration es 1 .

Nodo.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.

Pitón

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

El ejemplo de generación de miniaturas utiliza algunos de estos atributos para detectar casos de salida en los que la función devuelve:

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

Pitón

# 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

Descargar, transformar y cargar un archivo

En algunos casos, puede que no sea necesario descargar archivos de Cloud Storage. Sin embargo, para realizar tareas intensivas, como generar una imagen en miniatura a partir de un archivo almacenado en Cloud Storage, necesita descargar archivos a la instancia de funciones, es decir, la máquina virtual que ejecuta su código.

Al utilizar Cloud Functions junto con programas de procesamiento de imágenes como sharp para Node.js y Pillow para Python, puede realizar manipulaciones en archivos de imágenes gráficas. El siguiente es un ejemplo de cómo crear una imagen en miniatura para un archivo de imagen cargado:

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

Descargue el archivo a un directorio temporal en su instancia de Cloud Functions. En esta ubicación, puede procesar el archivo según sea necesario y luego cargarlo en Cloud Storage. Al realizar tareas asincrónicas, asegúrese de devolver una promesa de JavaScript en su devolución de llamada.

Pitón

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

Este código crea una miniatura de 200x200 para la imagen guardada en un directorio temporal y luego la vuelve a cargar en Cloud Storage.