Uploader une vidéo (ou image/audio) depuis Flutter vers Firebase via une Cloud Function
Quand on construit une application Flutter appuyée sur Firebase, l'upload de fichiers (vidéo, audio, image) est une fonctionnalité courante. Par défaut, beaucoup utilisent Firebase Storage directement depuis Flutter. Mais dans certains cas, il est préférable, voire indispensable, de passer par une Cloud Function : pour des raisons de sécurité, de traitement serveur, ou de logique métier avancée. Voici un exemple complet d'implémentation d'un upload binaire via backend Firebase.
Pourquoi ne pas utiliser Firebase Storage côté client ?
Firebase fournit un SDK très pratique pour uploader un fichier directement depuis une application Flutter. Cependant, cette approche a plusieurs limites :
- Il faut gérer les rôles et permissions d'accès finement.
- On ne peut pas encapsuler de logique métier (validation, traitement IA, etc.).
- Le fichier est envoyé tel quel, sans possibilité d'intervention serveur.
Dès qu'on souhaite plus de contrôle (vérifier l'utilisateur, transcrire la vidéo, générer une miniature, etc.), passer par une Cloud Function devient la meilleure option.
Côté Flutter : uploader un fichier avec Dio
Dans cet exemple, le fichier est sélectionné par l'utilisateur (via file_picker
, image_picker
, ou autre), puis envoyé sous forme de multipart/form-data
à une URL Firebase (Cloud Function exposée via HTTP).
videoFile est de type XFile.
Voici le code Flutter correspondant :
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,
},
),
);
On utilise ici Dio pour envoyer une requête POST multipart contenant le fichier. Le userId
est passé dans les headers pour être vérifié côté serveur.
Côté Firebase : parser le multipart/form-data
avec Busboy
Firebase ne supporte pas nativement le parsing de corps multipart
. Pour récupérer les fichiers envoyés depuis Flutter, on utilise donc la librairie Node.js busboy
. Elle permet d'extraire les champs de formulaire et les fichiers, même en mode streaming.
Installons 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 };
Une fois les fichiers extraits (sous forme de Buffer
), ils peuvent être stockés dans Firebase Storage via @google-cloud/storage
.
Intégration avec la logique métier
Dans notre cas d'usage, la vidéo est liée à une série. On passe donc les paramètres serieId
et language
dans l'URL. Côté serveur, on vérifie :
- que l'utilisateur est authentifié et a le droit d'uploader,
- que la requête est valide,
- et on appelle une commande
UploadSerieVideo
avec les dépendances Firebase (Firestore + Storage).
const { files } = await getParts(req);
const result = await command.execute({
serieId,
language,
videoFile: files[0]!.buf,
videoFileName: files[0]!.filename,
});
Cette approche permet d'encapsuler la logique métier, de valider les données, et de garantir que seul un utilisateur autorisé puisse déclencher cet upload.
Conclusion
Uploader un fichier depuis Flutter vers Firebase via une Cloud Function est une stratégie puissante pour ceux qui cherchent à construire un backend robuste, sûr, et facilement extensible. Cela permet d'ajouter des traitements intermédiaires, de protéger l'accès aux ressources, et de centraliser la logique métier dans un backend unique.