Un client nous demande si c’est faisable : filmer une vidéo d’une minute dans une app React Native, la compresser côté téléphone, et l’envoyer sur un serveur. La question n’est pas si c’est possible (ça l’est) mais si c’est fiable sur des fichiers de 50-200 Mo en 4G, et combien de temps ça prend.

On a monté un proof of concept pour répondre.

Le pipeline : pick, compress, upload

L’app fait trois choses. L’utilisateur choisit une vidéo depuis sa galerie via react-native-document-picker. On compresse la vidéo avec react-native-video-processing (un wrapper autour d’AVAssetExportSession sur iOS). Et on uploade le résultat en multipart via fetch.

// Compression : 640x480, pas d'audio, bitrate adapté
const { source, width, height } = await VideoPlayer.compress(uri, {
  width: 640,
  height: 480 * (height / width),  // préserve le ratio
  minimumBitrate: 300 * 1000,
  bitrateMultiplier: (640 * 480) / (width * height) * 7,
  removeAudio: true,
})

On cible 640x480 en sortie. Le bitrateMultiplier est proportionnel au ratio de réduction : plus on réduit la résolution, plus on peut baisser le bitrate sans perte visible. On coupe l’audio pour gagner encore 20-30% de taille. Une vidéo de 60 secondes filmée en 1080p (150 Mo) passe à 8-15 Mo après compression. C’est la différence entre “uploadable en 4G” et “l’utilisateur abandonne”.

Si la vidéo dépasse 60 secondes, on la tronque avant compression :

if (duration > 60 * 1000) {
  const trimmed = await VideoPlayer.trim(uri, {
    startTime: 0,
    endTime: 60 * 1000,
  })
  uri = trimmed.source
}

L’upload : un fetch et c’est tout

L’upload lui-même est un POST multipart classique. React Native supporte FormData nativement, et fetch sait envoyer un fichier local via son URI :

async function uploadVideo(uri, remoteUrl) {
  const ext = mime.extension(mime.getType(uri)) || "mp4"
  const type = mime.getType(uri) || "video/mp4"

  const formData = new FormData()
  formData.append("video", {
    uri,
    name: `video.${ext}`,
    type,
  })

  return await fetch(remoteUrl, {
    method: "POST",
    body: formData,
  })
}

C’est simple. C’est aussi limité. Pas de progress bar (le fetch de React Native ne supporte pas onUploadProgress), pas de reprise en cas de coupure réseau, pas de chunking. Pour un fichier de 10 Mo en wifi, ça passe sans problème. Pour 50 Mo en 4G instable, c’est un pari.

Le serveur de test : Express + Multer

Pour tester sans monter un vrai backend, on a un mini serveur Express avec Multer :

import express from "express"
import multer from "multer"
import morgan from "morgan"

const upload = multer({ dest: "uploads/" })
const app = express()

app.use(morgan("short"))
app.post("/upload", upload.single("video"), (req, res) => {
  const { path, mimetype, size } = req.file
  res.json({ path, mimetype, size })
})

app.listen(3000)

Multer gère le parsing multipart et sauvegarde le fichier sur disque. On pointe l’app React Native sur l’IP locale de la machine de dev. Ça permet de vérifier que le fichier arrive entier, avec le bon MIME type et la bonne taille.

Ce qu’on a appris

La compression fait toute la différence. Sans elle, uploader une vidéo de 60 secondes en 1080p est irréaliste sur mobile. Avec, on tombe à des tailles gérables. Le ratio 10:1 qu’on obtient (150 Mo → 15 Mo) est suffisant pour la plupart des cas d’usage.

Le fetch natif de React Native fonctionne pour l’upload multipart, mais il manque les progress events. Pour une vraie app, il faudrait passer par XMLHttpRequest (qui supporte upload.onprogress) ou une lib native dédiée.

La compression vidéo côté iOS est rapide (quelques secondes pour une minute de vidéo). Côté Android, react-native-video-processing utilise FFmpeg sous le capot et c’est plus lent. On n’a pas eu le temps de finaliser l’intégration Android sur ce POC.

Le POC a validé ce qu’on voulait : oui, on peut filmer, compresser et uploader une vidéo d’une minute en moins de 30 secondes au total (compression + upload wifi). En 4G, ça monte à 1-2 minutes selon la couverture. Le client a eu sa réponse.