Ecrit par Nathanaël Cherrier

Forcer le téléchargement d'un fichier en utilisant JavaScript

Publié dans

Partagez l'article

twitter facebook

Forcer le téléchargement d'un fichier en JavaScript peut sembler compliqué car aucune méthode native n'existe pour le faire. Si j'ai ma petite idée sur le pourquoi cela n'a pas été développé, il n'en reste pas moins des cas où l'on doit le faire. Je pense notamment au développement d'application avec Angular ou React.

Il y a quelques temps j'ai du me pencher sur la question pour une fonctionnalité d'export Excel demandée par un client. Il fallait que le téléchargement soit démarré par JavaScript mais pas comme une requête GET toute basique car le flux de données serait capté par le navigateur et transmis à JavaScript.

Le navigateur doit comprendre que le fichier est destiné à être téléchargé par l'utilisateur.

L'astuce que j'ai trouvé est d'utiliser une bonne vieille balise ancre <a href="" />  que je ne lie pas au DOM. Celle-ci n'est donc pas rendue et ne fait pas partie de la structure HTML du projet. Par contre, elle existe belle et bien en tant qu'objet JavaScript et est donc parfaitement utilisable.

Voici ma fonction :

function downloadFile(data, name = 'file', type = 'text/plain') {
  const anchor = document.createElement('a')
  anchor.href = window.URL.createObjectURL(new Blob([data], { type }))
  anchor.download = name
  anchor.click()
}

On a besoin du nom à donner au fichier et des données du fichier. Les données peuvent venir d'une requête faite avant, d'une génération d'image automatique, etc.

On crée ensuite un objet Blob à partir des données brutes.  Et on indique la cible de l'ancre comme étant une URL locale représentant ce Blob .

On ajoute à l'ancre un attribut download qui va permettre d'indiquer que l'on veut télécharger la source et sous quel nom.

Enfin, on simule le click sur cette ancre pour déclencher son comportement normal, entièrement géré par le navigateur.

Optimisation de la fonction

La fonction ci-dessus fait le boulot mais je pense qu'elle peut être améliorer pour prendre en compte plus de cas d'utilisation, pour être plus simple à lire, etc.

function downloadFile(data, name = 'file', type = 'text/plain') {
  const { createElement } = document
  const { URL: { createObjectURL, revokeObjectURL }, setTimeout } = window

  const blob = new Blob([data], { type })
  const url = createObjectURL(blob)

  const anchor = createElement('a')
  anchor.setAttribute('href', url)
  anchor.setAttribute('download', name)
  anchor.click()
  
  setTimeout(() => { revokeObjectURL(url) }, 100)
}

Cette version optimisée de downloadFile fonctionne avec tous les navigateurs récents. J'ai donc volontairement exclu Internet Explorer ici.

Si vous avez absolument besoin du support d'IE, ajoutez un test pour la méthode navigator.msSaveOrOpenBlob qui est la seule à pouvoir ouvrir et sauvegarder des blobs sur IE.

Article édité suite aux retours d'un lecteur. Merci à lui et à tous ceux qui prennent le temps de faire des retours constructifs.

Si vous avez des questions ou des remarques/conseils, n'hésitez pas à en discuter avec les autres membres du forum (zone de commentaires plus bas)! Et si vous aimez l'article, n'oubliez pas de le partager avec vos amis. Vous pouvez aussi soutenir le blog sur Patreon.