Créer des archives ZIP dans le navigateur avec JSZip
Transmettez les objets File du navigateur à JSZip, générez un Blob ZIP, puis confiez ce Blob à un lien
de téléchargement. La page complète ci-dessous permet aux utilisateurs de choisir ou de déposer
plusieurs fichiers locaux et de les télécharger sous la forme archive.zip, avec un suivi de la
progression et une limite sur la taille totale des fichiers d’entrée.
Cet exemple ne téléverse pas les fichiers sélectionnés et ne nécessite pas de serveur de compression. Il charge JSZip depuis un CDN, une connexion internet est donc nécessaire pour charger la bibliothèque. La compression et la mémoire qu’elle consomme restent dans le navigateur ; cette approche convient à des ensembles modestes de fichiers déjà présents sur l’appareil de l’utilisateur.
Exécuter l’exemple complet dans le navigateur
Enregistrez ce code dans un nouveau fichier index.html au sein d’un dossier vide, puis ouvrez-le dans
Chrome pour ordinateur. Aucune étape de compilation ni aucun serveur local n’est nécessaire. Le script
fixe la version de JSZip à 3.10.2 ; cet exemple a été testé dans Chrome 153 sous macOS.
Choisissez des fichiers individuels avec le sélecteur, ou déposez-les à l’intérieur du fieldset. Chaque sélection remplace l’ensemble précédent, dont les noms s’affichent sous le sélecteur. Le parcours des dossiers n’est pas inclus. Cliquez sur Create ZIP, attendez la demande de téléchargement, puis ouvrez le ZIP depuis les téléchargements de votre navigateur.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Create a ZIP with JSZip</title>
</head>
<body>
<h1>Create a ZIP</h1>
<fieldset id="dropZone">
<legend>Choose files or drop them here</legend>
<label for="fileInput">Files to archive</label>
<input type="file" id="fileInput" multiple />
<p id="selection">No files selected.</p>
<button type="button" id="zipButton">Create ZIP</button>
</fieldset>
<p id="progress" role="status">Select files totaling at most 100 MiB.</p>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.2/dist/jszip.min.js"></script>
<script>
const fileInput = document.getElementById('fileInput')
const dropZone = document.getElementById('dropZone')
const selection = document.getElementById('selection')
const progress = document.getElementById('progress')
let selectedFiles = []
let busy = false
function selectFiles(files) {
if (busy) return
selectedFiles = Array.from(files)
// The names below remain visible; clearing the picker allows reselection.
fileInput.value = ''
selection.textContent = selectedFiles.length
? `Selected files: ${selectedFiles.map((file) => file.name).join(', ')}`
: 'No files selected.'
progress.textContent = 'Ready to create ZIP.'
}
fileInput.addEventListener('change', () => selectFiles(fileInput.files))
dropZone.addEventListener('dragover', (event) => {
event.preventDefault()
event.dataTransfer.dropEffect = busy ? 'none' : 'copy'
})
dropZone.addEventListener('drop', (event) => {
event.preventDefault()
selectFiles(event.dataTransfer.files)
})
async function createZip() {
if (busy) return
const files = selectedFiles.slice()
if (files.length === 0) {
progress.textContent = 'Select or drop files first.'
return
}
if (typeof JSZip === 'undefined') {
progress.textContent = 'JSZip could not load. Check your connection and reload.'
return
}
const totalSize = files.reduce((sum, file) => sum + file.size, 0)
if (totalSize > 100 * 1024 * 1024) {
progress.textContent = 'Select at most 100 MiB of files.'
return
}
const names = new Set()
for (const file of files) {
if (names.has(file.name)) {
progress.textContent = `Duplicate filename: ${file.name}. Rename it first.`
return
}
names.add(file.name)
}
busy = true
dropZone.disabled = true
progress.textContent = 'Generating ZIP…'
try {
const zip = new JSZip()
for (const file of files) zip.file(file.name, file)
const blob = await zip.generateAsync(
{
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 6 },
},
({ percent }) => {
progress.textContent = `Generating ZIP: ${Math.round(percent)}%`
},
)
const url = URL.createObjectURL(blob)
// Leave time for the browser to consume the URL before releasing it.
setTimeout(() => URL.revokeObjectURL(url), 60_000)
const link = document.createElement('a')
link.href = url
link.download = 'archive.zip'
document.body.appendChild(link)
link.click()
link.remove()
progress.textContent = 'ZIP download requested. Check your browser’s downloads.'
} catch {
progress.textContent = 'Could not create ZIP. Reselect the files and try again.'
} finally {
busy = false
dropZone.disabled = false
}
}
document.getElementById('zipButton').addEventListener('click', createZip)
</script>
</body>
</html>
Pour une vérification rapide, sélectionnez un fichier texte et une image portant des noms différents.
Le ZIP téléchargé devrait contenir les deux à sa racine, avec leur contenu inchangé. Générez-le de
nouveau pour demander un autre téléchargement. archive.zip est un nom suggéré : le navigateur contrôle
l’emplacement d’enregistrement et toute invite de renommage ou de remplacement lorsque ce nom existe
déjà. La page n’impose pas l’écrasement du fichier existant.
Ajouter les fichiers directement et signaler la progression de la génération
Un champ de sélection de fichiers et un dépôt de fichiers fournissent tous deux des objets File
via la File API. La méthode file(name, data) de JSZip
les accepte directement, car un File est une forme de Blob. Vous n’avez pas besoin d’un wrapper
FileReader distinct ni d’une conversion en base64 pour ajouter des fichiers locaux à l’archive.
generateAsync()
renvoie une promesse pour l’archive terminée. Ici, cet appel produit un Blob et utilise DEFLATE au
niveau six. Le percent du callback décrit la génération de l’archive, et non la progression d’un
téléchargement. Une fois le Blob prêt, une URL temporaire le met à disposition du lien ; le minuteur
libère cette URL après la demande de téléchargement.
Le message « ZIP download requested » ne signifie volontairement pas « enregistré ». La
propriété download du lien
ne confirme pas qu’un téléchargement a eu lieu. Les paramètres du navigateur peuvent le bloquer, ou
l’utilisateur peut annuler la boîte de dialogue d’enregistrement. Consultez le panneau des
téléchargements si aucun fichier n’apparaît.
Garder une seule tâche ZIP active
Le garde-fou busy est activé avant le début de la compression. Tant que la promesse est en
attente, le fieldset désactive le sélecteur et le bouton, et selectFiles() ignore les nouveaux dépôts et
événements change. La tâche conserve sa sélection d’origine : un second clic ne peut donc ni lancer
une autre archive ni remplacer le message de progression. Après une réussite ou un échec,
finally réactive les contrôles. Pour utiliser une autre sélection, attendez la fin de la tâche en
cours, puis sélectionnez ou déposez les nouveaux fichiers.
JSZip met à jour une entrée existante lorsque le même nom est ajouté de nouveau. La vérification des doublons empêche qu’un fichier sélectionné en remplace silencieusement un autre, ce qui compte lorsque des fichiers issus de dossiers différents partagent un même nom de base. Renommez l’un d’eux avant de réessayer. Des noms qui ne diffèrent que par la casse peuvent tout de même entrer en collision lors de l’extraction sur un système de fichiers insensible à la casse.
Si le CDN ne peut pas être chargé, la page vous demande de vérifier la connexion. Si la lecture ou la compression échoue, elle vous demande de sélectionner à nouveau les fichiers. Une tâche en échec ne déclenche aucune demande de téléchargement. Pour une page qui doit fonctionner hors ligne, servez le bundle JSZip à version fixée avec votre propre page au lieu de dépendre du CDN.
Gestion des fichiers volumineux et considérations de performance
La vérification de 100 MiB est un exemple de politique d’entrée, et non une garantie de capacité du
navigateur. Dans JSZip,
generateAsync() conserve le résultat complet en mémoire,
et la lecture puis la compression des entrées nécessitent aussi de la mémoire. De nombreux petits
fichiers ajoutent également une surcharge. Choisissez une limite plus basse si vos appareils cibles
ne peuvent pas gérer confortablement cette charge de travail.
DEFLATE peut réduire la taille d’un texte répétitif, mais les images et vidéos déjà compressées n’en
tirent parfois que peu de bénéfice. Si vous avez seulement besoin de regrouper ces fichiers, JSZip
prend aussi en charge compression: 'STORE', qui ignore la compression. Déplacer la compression dans un Web
Worker peut éviter d’occuper le thread principal de la page, mais generateAsync({ type: 'blob' }) n’écrit pas pour autant
son résultat en flux vers le disque. Les exports volumineux nécessitent une autre stratégie de
mémoire et de sortie.
Compatibilité des navigateurs
La cible vérifiée ici est Chrome pour ordinateur sous macOS. Les autres navigateurs doivent prendre
en charge la File API, les promesses, async/await, les URL de Blob et le téléchargement depuis
ces URL. Conservez le sélecteur de fichiers pour les appareils qui ne permettent pas de glisser des
fichiers, et testez la sélection, la reprise après erreur et les téléchargements réels dans chaque
navigateur que vous comptez prendre en charge. La prise en charge de la bibliothèque JSZip ne suffit
pas, à elle seule, à établir que tout le parcours de téléchargement de la page y fonctionne.
