Subir un vídeo (o imagen/audio) desde Flutter a Firebase vía una Cloud Function

Cuando se construye una aplicación Flutter apoyada en Firebase, la subida de archivos (vídeo, audio, imagen) es una funcionalidad habitual. Por defecto, muchos usan Firebase Storage directamente desde Flutter. Pero en ciertos casos, es preferible, incluso indispensable, pasar por una Cloud Function : por razones de seguridad, de procesamiento en el servidor, o de lógica de negocio avanzada. Aquí hay un ejemplo completo de implementación de una subida binaria a través del backend Firebase.
¿Por qué no usar Firebase Storage en el cliente ?
Firebase proporciona un SDK muy práctico para subir un archivo directamente desde una aplicación Flutter. Sin embargo, este enfoque tiene varias limitaciones :
- Es necesario gestionar los roles y permisos de acceso de forma granular.
- No se puede encapsular la lógica de negocio (validación, procesamiento IA, etc.).
- El archivo se envía tal cual, sin posibilidad de intervención del servidor.
En cuanto se desea más control (verificar al usuario, transcribir el vídeo, generar una miniatura, etc.), pasar por una Cloud Function se convierte en la mejor opción.
En Flutter : subir un archivo con Dio
En este ejemplo, el archivo es seleccionado por el usuario (vía file_picker, image_picker, u otro), y luego enviado en forma de multipart/form-data a una URL Firebase (Cloud Function expuesta vía HTTP).
videoFile es de tipo XFile.
Aquí está el código Flutter correspondiente :
final bytes = await videoFile.readAsBytes();
final formData = FormData.fromMap({
'video': MultipartFile.fromBytes(
bytes,
filename: videoFile.name,
contentType: DioMediaType.parse(videoFile.mimeType ?? 'video/mp4'),
),
});
final response = await dio.post(
_url,
data: formData,
queryParameters: {
'serieId': serieId,
'language': language,
},
options: Options(
headers: {
'Content-Type': 'multipart/form-data',
'x-user-id': userId,
},
),
);
Aquí usamos Dio para enviar una solicitud POST multipart que contiene el archivo. El userId se pasa en los encabezados para ser verificado en el servidor.
En Firebase : analizar el multipart/form-data con Busboy
Firebase no soporta de forma nativa el parseo del cuerpo multipart. Para recuperar los archivos enviados desde Flutter, se utiliza la librería de Node.js busboy. Esta permite extraer los campos del formulario y los archivos, incluso en modo streaming.
Instalemos busboy :
npm install busboy
import busboy from "busboy";
import { buffer } from "stream/consumers";
import { Request } from "firebase-functions/v2/https";
import { Response } from "firebase-functions/v1";
import { Readable } from "stream";
interface FilePart {
fieldName: string;
filename: string;
buf: Buffer;
}
interface FormParts {
fields: Record<string, string>;
files: FilePart[];
}
function getParts(req: Request): Promise<FormParts> {
const bb = busboy({ headers: req.headers });
const fields: Record<string, string> = {};
const files: { fieldName: string; filename: string; buf: Promise<Buffer> }[] =
[];
bb.on("field", (fieldName: string, val: string) => (fields[fieldName] = val));
bb.on(
"file",
(fieldName: string, file: Readable, { filename }: { filename: string }) => {
if (filename) {
files.push({ fieldName, filename, buf: buffer(file) });
} else {
file.resume();
}
}
);
return new Promise((resolve) => {
bb.on("finish", () =>
Promise.all(files.map((o) => o.buf)).then((ar) => {
const processedFiles: FilePart[] = files.map((o, i) => ({
fieldName: o.fieldName,
filename: o.filename,
buf: ar[i] || Buffer.alloc(0), // Provide a default empty buffer if undefined
}));
resolve({ fields, files: processedFiles });
})
);
bb.end(req.rawBody);
});
}
export { getParts, addCorsHeaders };
Una vez que los archivos están extraídos (en forma de Buffer), pueden almacenarse en Firebase Storage mediante @google-cloud/storage.
Integración con la lógica de negocio
En nuestro caso de uso, el vídeo está vinculado a una serie. Por eso pasamos los parámetros serieId y language en la URL. En el servidor, se verifica :
- que el usuario esté autenticado y tenga derecho a subir archivos,
- que la solicitud sea válida,
- y se invoca un comando
UploadSerieVideocon las dependencias de Firebase (Firestore + Storage).
const { files } = await getParts(req);
const result = await command.execute({
serieId,
language,
videoFile: files[0]!.buf,
videoFileName: files[0]!.filename,
});
Este enfoque permite encapsular la lógica de negocio, validar los datos y garantizar que solo un usuario autorizado pueda iniciar esta subida.
Conclusión
Subir un archivo desde Flutter a Firebase vía una Cloud Function es una estrategia poderosa para quienes buscan construir un backend robusto, seguro y fácilmente extensible. Esto permite añadir tratamientos intermedios, proteger el acceso a los recursos y centralizar la lógica de negocio en un único backend.
Comentarios
Cargando...