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

Cet article est libre d'accès pour tous grâce à la générosité des abonnés de Mindsers Blog qui soutiennent notre blog indépendant. Si vous appréciez le contenu que je propose, je vous invite à vous abonner dès aujourd'hui.

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.

Join 100+ developers and entrepreneurs and get notified on every new content.

No spam ever. Unsubscribe in a single click at any time.

Si vous avez des questions ou des remarques/conseils, n'hésitez pas à laisser un commentaire plus bas ! Je serais ravis de vous lire. Et si vous aimez l'article, n'oubliez pas de le partager avec vos amis.