Gatilhos do Cloud Storage


É possível acionar uma função em resposta ao upload, à atualização ou à exclusão de arquivos e pastas no Cloud Storage.

Os exemplos desta página são baseados em uma função de amostra acionada com o upload de arquivos de imagem no Cloud Storage. Essa função demonstra como acessar atributos de eventos, como baixar um arquivo para uma instância do Cloud Functions e outros princípios básicos do gerenciamento de eventos do Cloud Storage.

Importar os módulos necessários

Para começar, importe o módulo necessário para processar eventos do Cloud Storage:

Node.js

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

Python

 from firebase_functions import storage_fn

Para criar a amostra completa, adicione também as dependências do Firebase Admin SDK e das ferramentas de processamento de imagem:

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

Definir o escopo de uma função do Cloud Storage

Use o seguinte padrão para definir o escopo da sua função para um bucket Cloud Storage específico e definir as opções pretendidas:

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

Em contrapartida, a função de gerador de miniaturas neste exemplo é dimensionada de acordo com o bucket padrão do projeto:

Node.js

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

Python

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

Definir o local da função

Uma incompatibilidade entre locais pode resultar em falha na implantação. Além disso, a distância entre o local de um bucket do Cloud Storage e o local da função pode criar uma latência de rede significativa. Para evitar essas situações, especifique o local da função para que corresponda ao local do bucket/acionador de uma das seguintes maneiras:

  • O local da função é igual ao local do gatilho
  • O local da função está dentro do local do gatilho (quando a região do gatilho é dual/multirregional)
  • A função poderá estar em qualquer local se a região do gatilho estiver definida como us-central1

Gerenciar eventos do Cloud Storage

Estes gerenciadores para responder a eventos do Cloud Storage estão disponíveis:

Node.js

  • onObjectArchived: enviado apenas depois que o controle de versões do objeto é ativado no bucket. Este evento indica que a versão ativa de um objeto se tornou uma versão arquivada, podendo ter sido arquivada ou substituída pelo upload de um objeto de mesmo nome.
  • onObjectDeleted: enviado quando um objeto é excluído permanentemente. Isso inclui objetos que são substituídos ou excluídos como parte da configuração do ciclo de vida do bucket. Nos buckets com o controle de versões de objetos ativado, ele não é enviado quando um objeto é arquivado (ver onArchive), mesmo que o arquivamento ocorra pelo método storage.objects.delete.
  • onObjectFinalized: enviado quando um novo objeto ou nova geração de um objeto é criada com sucesso no bucket. Isso inclui copiar ou reescrever um objeto. Um upload com falha não aciona esse evento.
  • onMetadataUpdated: enviado quando os metadados de um objeto são modificados.

Python

  • on_object_archived: enviado apenas depois que o controle de versões do objeto é ativado no bucket. Este evento indica que a versão ativa de um objeto se tornou uma versão arquivada, podendo ter sido arquivada ou substituída pelo upload de um objeto de mesmo nome.
  • on_object_deleted: enviado quando um objeto é excluído permanentemente. Isso inclui objetos que são substituídos ou excluídos como parte da configuração do ciclo de vida do bucket. Nos buckets com o controle de versões de objetos ativado, ele não é enviado quando um objeto é arquivado (ver onArchive), mesmo que o arquivamento ocorra pelo método storage.objects.delete.
  • on_object_finalized: enviado quando um novo objeto ou nova geração de um objeto é criada com sucesso no bucket. Isso inclui copiar ou reescrever um objeto. Um upload com falha não aciona esse evento.
  • on_metadata_updated: enviado quando os metadados de um objeto são modificados.

Acessar atributos de objetos do Cloud Storage

O Cloud Functions expõe vários atributos de objetos do Cloud Storage como o tamanho do objeto e o tipo de conteúdo para o arquivo atualizado. O atributo do metageneration será incrementado sempre que houver uma alteração nos metadados do objeto. Para novos objetos, o valor de 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

A amostra de geração de miniaturas usa alguns desses atributos para detectar casos de saída em que a função retorna:

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

Fazer o download, transformar e fazer upload de um arquivo

Para alguns casos, pode não ser necessário baixar arquivos do Cloud Storage. Mas, para executar tarefas intensivas, como gerar uma imagem de miniatura usando um arquivo armazenado no Cloud Storage, é necessário baixar os arquivos para a instância das funções, ou seja, para a máquina virtual que executa seu código.

Ao usar o Cloud Functions com programas de processamento de imagens como o sharp para Node.js e a Pillow para Python, é possível fazer manipulações em arquivos de imagens gráficas. Veja abaixo um exemplo de como criar uma imagem em miniatura para um arquivo de imagem:

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

Baixe o arquivo para um diretório temporário na sua instância do Cloud Functions. Nesse local, é possível processar o arquivo conforme necessário e depois fazer upload para o Cloud Storage. Ao executar tarefas assíncronas, retorne uma promessa de JavaScript na callback.

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

Esse código cria uma miniatura de 200x200 para a imagem salva em um diretório temporário e, em seguida, faz upload dela de volta ao Cloud Storage.