next-file-downloader
v1.1.0
Published
Universal file download library for Next.js, React, and browser with automatic CORS fallback, proxy streaming route, and progress tracking.
Maintainers
Readme
next-file-downloader 🚀
Package tout-en-un pour télécharger tout type de fichier en Next.js, React et Node.js sans se casser la tête avec les CORS, les URLs cloud/S3 ou la gestion des streams.
🎯 Pourquoi cette librairie ?
Dans les projets Next.js ou React, un backend envoie très souvent une URL vers un stockage cloud (ex: AWS S3, Cloudflare R2, Google Cloud Storage, Supabase, Scaleway) :
{
"resumeUrl": "https://my-bucket.s3.eu-west-1.amazonaws.com/candidates/cv/sample-candidate-resume.pdf"
}Quand vous tentez de le télécharger depuis le navigateur :
<a href={url} download>ne fonctionne pas pour les URLs cross-origin : le navigateur ouvre le PDF au lieu de déclencher le téléchargement.fetch(url) -> blob()échoue avec une erreur CORS si le bucket S3 n'a pas configuré les headers spécifiques.- Écrire à chaque fois une API route manuellement dans Next.js (gérer les headers
Content-Disposition, les streams et le typage) devient vite répétitif et fatiguant.
next-file-downloader résout tout cela :
- ✅ Route API Next.js prête en 1 ligne (
createDownloadRoute) avec streaming natif et protection SSRF. - ✅ Fonction client universelle (
downloadFile) avec détection automatique et repli vers le proxy en cas de CORS. - ✅ Hook React complet (
useFileDownload) avec état de chargement, progression en pourcentage (0-100%) et annulation (abort). - ✅ Déduction intelligente du nom de fichier (depuis le header HTTP
Content-Disposition, l'URL ou un nom personnalisé). - ✅ Zéro dépendance lourde, TypeScript first, compatible Next.js App Router & Pages Router.
📦 Installation
npm install next-file-downloader
# ou
yarn add next-file-downloader
# ou
pnpm add next-file-downloader🚀 Utilisation rapide
1. En une ligne de code (Côté Client)
import { downloadFile } from 'next-file-downloader';
// Télécharge et extrait automatiquement le nom depuis l'URL cloud
await downloadFile('https://my-bucket.s3.eu-west-1.amazonaws.com/candidates/cv/sample-candidate-resume.pdf');
// Ou avec un nom personnalisé :
await downloadFile(resumeUrl, {
filename: 'CV_Candidat.pdf',
});2. Avec la Route Proxy Next.js (Recommandé pour Cloud & S3)
Si votre bucket n'a pas activé les CORS ou si vous voulez garantir le téléchargement sur tous les navigateurs sans jamais être bloqué :
👉 Créez votre route Next.js App Router :
Créez le fichier src/app/api/download/route.ts (ou app/api/download/route.ts) :
// src/app/api/download/route.ts
import { createDownloadRoute } from 'next-file-downloader/server';
// Par défaut, accepte TOUTES les URLs venant de vos backends (app..., api..., cloud, etc.)
export const { GET, POST } = createDownloadRoute({
// Optionnel : forwarde automatiquement les tokens d'authentification (Bearer, etc.)
forwardHeaders: true,
// Optionnel : si vous souhaitez restreindre à certains domaines spécifiques
// allowedDomains: ['*.amazonaws.com', 'app.monsite.com'],
});👉 Appelez-le simplement depuis votre composant :
import { downloadFile } from 'next-file-downloader';
await downloadFile(resumeUrl, {
filename: 'Candidature_CV.pdf',
proxyUrl: '/api/download', // Utilise le proxy ou s'y replie automatiquement en cas de CORS !
});3. Avec le Hook React (useFileDownload)
Idéal pour afficher un bouton avec spinner et pourcentage de téléchargement :
'use client';
import { useFileDownload } from 'next-file-downloader/react';
export function DownloadCvButton({ resumeUrl }: { resumeUrl: string }) {
const { download, isDownloading, progress, error } = useFileDownload({
proxyUrl: '/api/download',
});
return (
<div>
<button
disabled={isDownloading}
onClick={() =>
download(resumeUrl, {
filename: 'CV_Collaborateur.pdf',
})
}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50"
>
{isDownloading
? `Téléchargement ${progress?.percent ? `(${progress.percent}%)` : '...'}`
: 'Télécharger le CV'}
</button>
{error && <p className="text-red-500 text-sm mt-1">Erreur: {error.message}</p>}
</div>
);
}🛠️ Modes de téléchargement
L'option mode vous permet de contrôler la stratégie :
| Mode | Description |
| :--- | :--- |
| 'auto' (défaut) | Tente le téléchargement direct. Si une erreur CORS ou réseau survient et qu'un proxyUrl est fourni, bascule automatiquement sur le proxy. |
| 'proxy' | Force le passage par l'API Route Next.js (proxyUrl). Idéal pour les buckets privés ou sans CORS. |
| 'direct' | Utilise un tag DOM <a href="..." download> classique. |
| 'blob' | Télécharge en mémoire sous forme de Blob et déclenche la sauvegarde. |
await downloadFile(url, {
mode: 'proxy',
proxyUrl: '/api/download',
filename: 'Rapport.xlsx',
});📑 Utilitaires supplémentaires
import {
downloadBlob,
downloadBase64,
getFilenameFromUrl,
sanitizeFilename,
} from 'next-file-downloader';
// 1. Extraire le nom de fichier d'une URL :
const name = getFilenameFromUrl('https://my-bucket.s3.amazonaws.com/docs/mon-fichier.pdf?auth=123');
// -> "mon-fichier.pdf"
// 2. Nettoyer un nom de fichier pour éviter les caractères invalides :
const safe = sanitizeFilename('CV / Candidat : 2026?.pdf');
// -> "CV _ Candidat _ 2026_.pdf"
// 3. Télécharger un Blob déjà existant :
downloadBlob(myBlob, 'export.csv');
// 5. Forcer le téléchargement direct sur S3/Cloud sans route API :
import { appendS3DownloadParam } from 'next-file-downloader';
const directUrl = appendS3DownloadParam('https://my-bucket.s3.amazonaws.com/cv.pdf', 'Mon_CV.pdf');
// -> 'https://my-bucket.s3.amazonaws.com/cv.pdf?response-content-disposition=attachment%3B+filename%3D%22Mon_CV.pdf%22'💡 Comment se passer à 100% de l'API Route ?
Si vous ne voulez aucune route API serveur et faire 100% des téléchargements directement dans le navigateur client, vous avez 2 solutions :
Solution 1 : Activer les CORS sur votre Cloud Storage (Recommandé - 2 minutes)
Par défaut, les buckets AWS S3, Scaleway Object Storage, Cloudflare R2 ou Supabase bloquent les requêtes venant d'un navigateur pour des raisons de sécurité.
Il vous suffit de coller cette règle CORS dans la console de votre fournisseur (AWS / Scaleway / Cloudflare R2 / Supabase) :
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["Content-Disposition", "Content-Length"],
"MaxAgeSeconds": 3600
}
]👉 Résultat : Dès que cette règle est activée sur votre bucket, vous n'avez plus besoin d'aucune API route ! Le navigateur télécharge directement :
await downloadFile('https://mon-bucket.s3.../fichier.pdf', {
filename: 'Rapport.pdf',
});Solution 2 : Utiliser le paramètre S3 response-content-disposition
AWS S3, Scaleway, Google Cloud et Cloudflare R2 supportent un paramètre d'URL natif :
?response-content-disposition=attachment; filename="mon-fichier.pdf"
Quand ce paramètre est présent dans l'URL, le serveur Cloud Storage renvoie automatiquement l'ordre au navigateur de télécharger le fichier (au lieu de l'afficher dans un onglet), sans aucun problème de CORS et sans aucune API route !
Côté client avec next-file-downloader :
// Téléchargement direct avec injection automatique du paramètre S3 :
await downloadFile(s3Url, {
filename: 'Mon_CV.pdf',
mode: 'direct', // ou fallbackToDirect: true
});Ou côté backend si votre backend génère des URLs pré-signées :
// Exemple Backend (AWS SDK / Scaleway) :
const url = await getSignedUrl(s3Client, new GetObjectCommand({
Bucket: 'mon-bucket',
Key: 'cv.pdf',
ResponseContentDisposition: 'attachment; filename="Mon_CV.pdf"',
}));Le navigateur téléchargera alors directement le fichier sans avoir besoin d'un proxy serveur !
🔒 Sécurité du Serveur (next-file-downloader/server)
La fonction createDownloadRoute intègre de base :
- Protection anti-SSRF : blocage automatique des adresses locales (
localhost,127.0.0.1, adresses IP privées10.x,192.168.x, métadonnées cloud169.254.169.254). - Support des Wildcards : ex.
allowedDomains: ['*.amazonaws.com', '*.my-storage.com']. - Streaming sans mise en cache RAM : les fichiers volumineux ne saturent pas la mémoire du serveur Node / Next.js.
- Support complet UTF-8 : les noms de fichiers avec accents et espaces sont préservés grâce à la norme RFC 5987 (
filename*=UTF-8''...).
Exemple personnalisé côté serveur :
import { handleDownloadRequest } from 'next-file-downloader/server';
export async function GET(req: Request) {
// Vos vérifications d'authentification éventuelles :
// const session = await auth();
// if (!session) return new Response('Unauthorized', { status: 401 });
return handleDownloadRequest(req, {
allowedDomains: ['*.amazonaws.com'],
maxSizeBytes: 50 * 1024 * 1024, // Limite à 50 Mo
});
}📄 Licence
MIT
