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

Cet article est libre d'accès pour tous grâce à ceux qui soutiennent notre blog indépendant.

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.

Rejoins 250+ développeurs de notre liste de diffusion et sois reçois les articles directement dans ta boite mail.

S'inscrire à la newsletter

Aucun spam. Désabonnes-toi en un seul clic à tout moment.

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.