Activadores de Cloud Storage


Puedes 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 ejemplo que se activa cuando los archivos de imagen se suben a Cloud Storage. Esta función de ejemplo muestra cómo acceder a atributos de eventos, cómo descargar un archivo a una instancia de Cloud Functions y otros aspectos fundamentales del control de eventos de Cloud Storage.

Importa los módulos requeridos

Para comenzar, importa el módulo necesario que te permita controlar los eventos de Cloud Storage:

Node.js

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

Python (versión preliminar)

 from firebase_functions import storage_fn

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

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 (versión preliminar)

 import io
import pathlib

from PIL import Image

from firebase_admin import initialize_app

initialize_app()
from firebase_admin import storage

Define el alcance de una función de Cloud Storage

Usa el siguiente patrón para definir el alcance de la función en un bucket específico de Cloud Storage y establecer las opciones que desees:

Node.js

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

Python (vista previa)

# 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, el alcance de la función de ejemplo del generador de miniaturas se limita al bucket predeterminado del proyecto:

Node.js

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

Python (versión preliminar)

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

Configura la ubicación de la función

Una discrepancia entre las ubicaciones puede provocar un error de implementación. Además, la distancia entre la ubicación de la función y la ubicación del bucket de Cloud Storage puede generar una latencia de red considerable. Para evitar estas situaciones, especifica la ubicación de la función para que coincida con la ubicación del bucket o del activador de una de las siguientes maneras:

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

Controla eventos de Cloud Storage

Estos son los controladores disponibles para responder a eventos de Cloud Storage:

Node.js

  • onObjectArchived: Solo se envía cuando un bucket tiene habilitado el control de versiones de objetos. Este evento señala que la versión publicada de un objeto se convirtió en una versión archivada, ya sea porque se la archivó o porque se reemplazó cuando se subió un objeto con el mismo nombre.
  • onObjectDeleted: Se envía cuando se borra un objeto de manera permanente. Incluye los objetos que se reemplazan o se borran según la configuración del ciclo de vida del bucket. En el caso de los buckets que tienen habilitado el control de versiones de objetos, este evento no se envía cuando se archiva un objeto (consulta onArchive), incluso si el archivado se lleva a cabo con el método storage.objects.delete.
  • onObjectFinalized: Se envía cuando se crea correctamente un objeto nuevo (o una nueva generación de un objeto existente) en el bucket. Incluye la copia o la reescritura de un objeto existente. Una carga con errores no activa este evento.
  • onMetadataUpdated: Se envía cuando cambian los metadatos de un objeto existente.

Python (vista previa)

  • on_object_archived: Solo se envía cuando un bucket tiene habilitado el control de versiones de objetos. Este evento señala que la versión publicada de un objeto se convirtió en una versión archivada, ya sea porque se la archivó o porque se reemplazó cuando se subió un objeto con el mismo nombre.
  • on_object_deleted: Se envía cuando se borra un objeto de manera permanente. Incluye los objetos que se reemplazan o se borran según la configuración del ciclo de vida del bucket. En el caso de los buckets que tienen habilitado el control de versiones de objetos, este evento no se envía cuando se archiva un objeto (consulta onArchive), incluso si el archivado se lleva a cabo con el método storage.objects.delete.
  • on_object_finalized: Se envía cuando se crea correctamente un objeto nuevo (o una nueva generación de un objeto existente) en el bucket. Incluye la copia o la reescritura de un objeto existente. Una carga con errores no activa este evento.
  • on_metadata_updated: Se envía cuando cambian los metadatos de un objeto existente.

Accede a los atributos de objetos de Cloud Storage

Cloud Functions expone varios atributos de los objetos de Cloud Storage, como el tamaño del objeto y el tipo de contenido del archivo actualizado. El atributo metageneration aumenta siempre que se produce un cambio en los metadatos del objeto. En el caso de los objetos nuevos, el valor de metageneration es 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 (vista previa)

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

La muestra de generación de miniaturas utiliza algunos de estos atributos para detectar casos de salida en los que el resultado es el siguiente:

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 (vista previa)

# 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

Descarga, transforma y sube un archivo

En algunos casos, tal vez no sea necesario descargar archivos desde Cloud Storage. Sin embargo, para realizar tareas intensivas, como generar una imagen en miniatura desde un archivo almacenado en Cloud Storage, debes descargar archivos en la instancia de las funciones, es decir, en la máquina virtual que ejecuta el código.

Con Cloud Functions y los programas de procesamiento de imágenes como sharp para Node.js y Pillow para Python, puedes manipular archivos de imagen gráfica. El siguiente es un ejemplo de cómo crear una imagen en miniatura para un archivo de imagen subido:

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

Descarga el archivo a un directorio temporal en tu instancia de Cloud Functions. En esta ubicación, puedes procesar el archivo según sea necesario y, luego, subirlo a Cloud Storage. Cuando ejecutes tareas asíncronas, asegúrate de mostrar una promesa de JavaScript en tu devolución de llamada.

Python (vista previa)

@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 200 × 200 píxeles de la imagen guardada en un directorio temporal y, luego, la sube a Cloud Storage.