web-dev-qa-db-fra.com

L'attribut de téléchargement de balise d'ancrage HTML ne fonctionne pas dans Firefox pour les fichiers jpg et png

Dans mon application Web, j'ai aidé un utilisateur à télécharger tout type de document (.png, .jpg, .docx, .xls, ...) 
J'essaie d'implémenter la fonctionnalité de téléchargement pour ces documents .

Dans Google Chrome, si vous cliquez sur le lien de téléchargement, la boîte de dialogue Enregistrer s'affiche pour tous les documents ci-dessus .

Dans Mozilla Firefox pour docx et xls, tout se passe bien, la boîte de dialogue Enregistrer s'affiche mais la balise de téléchargement pour .png et .jpg ne fonctionne pas comme prévu: aucune boîte de dialogue de téléchargement ou d'enregistrement , il ouvre directement cette image.

Mon code:

<a href="/img/14340.jpg" download="14340.jpg">Download</a>

J'ai essayé presque toutes les solutions mentionnées sur stackoverflow et suggérées par Google. Mais la plupart d’entre eux disent que 'vérifier la version de Firefox' et d’autres modifications telles que: essayez d’ajouter l’élément au DOM avant de déclencher le clic

Supprimer le nom de fichier de la balise de téléchargement, il est de type booléen, etc.

J'ai aussi essayé la leçon w3schools sur les balises d'ancrage et les attributs de téléchargement mais rien ne semble fonctionner.

Ma version de Mozilla Firefox est la suivante: 38.0.5

P.S .: en chrome ainsi que dans firefox .docs, .xls, documents .pdf fonctionnent très bien, le problème concerne .png et .jpg dans firefox.

12
tejas033

Firefox gérera les fichiers png et jpeg en utilisant la gestion par défaut, qui consiste à les insérer dans le document. Lorsque vous cliquez sur un lien, même si l'attribut de téléchargement est défini, Firefox semble penser qu'il a une nouvelle image, ignorant ainsi l'aspect du téléchargement. Cela peut être un bug temporaire.

Voici un moyen, certes pas très élégant, de contourner ce problème en obligeant l’image à être interprétée comme un flux d’octets.

Cela ne fonctionne pas inline sur Stackoverflow, vous devez donc le tester sur jsFiddle.

Le code fait ce qui suit:

  • Analyse le document pour les balises a.
  • Ceux qui ont data-link set auront un gestionnaire de clics commun attaché.
  • Lorsque vous cliquez dessus, le lien est extrait de l'attribut data-link (href est identique à #), chargé en tant que ArrayBuffer via XHR (les exigences CORS s'appliquent, ce n'est pas un problème dans ce cas) et est converti en une URL d'objet avec le blob défini sur type-mime octet/stream
  • L'URL de l'objet est défini sur window.location pour être redirigé vers ces données binaires, ce qui obligera le navigateur à demander à l'utilisateur de télécharger le fichier.
var links = document.querySelectorAll("a"), i = 0, lnk;

while(lnk = links[i++]) {
  if (lnk.dataset.link.length) lnk.onclick = toBlob;
}

function toBlob(e) {
  e.preventDefault();
  var lnk = this, xhr = new XMLHttpRequest();
  xhr.open("GET", lnk.dataset.link);
  xhr.responseType = "blob";
  xhr.overrideMimeType("octet/stream");
  xhr.onload = function() {
    if (xhr.status === 200) {
      window.location = (URL || webkitURL).createObjectURL(xhr.response);
    }
  };
  xhr.send();
}

Exemple de balise:

<a href="#" data-link="image.jpg">Click to download</a>

L'inconvénient est que vous perdrez l'extension dans le nom du fichier.

Cela est également possible d'utiliser une URL de données, mais une URL de données a un temps système supplémentaire de 166% par rapport à l'utilisation d'ArrayBuffer et d'un blob.

4
user1693593

J'ai eu un problème similaire avec Firefox qui ne gère pas l'attribut de téléchargement, même pour les fichiers du même domaine. 

Mes fichiers cible sont en fait hébergés sur AWS, ils sont donc inter-domaines. J'ai contourné cela avec un point de terminaison du même domaine qui télécharge le fichier distant et le redirige vers le client.

const express = require('express')
const {createWriteStream} = require('fs')
const downloadVideo = (url) => { return new Promise((resolve, reject) => {
  const filePath = `/tmp/neat.mp4`
  const ws = createWriteStream(filePath)
  request(url, {}, (error, response, body) => {
    if(error) { return reject(error) }
    resolve(filePath)
  }).pipe(ws)
})}

app.get('/api/download', async (req, res) => {
  const videoPath = await downloadVideo(req.query.url)
  res.sendFile(videoPath)
})

Sur le client, j'envoie le chemin du fichier au noeud final de téléchargement pour récupérer un blob, qui est ensuite converti en une URL d'objet. A partir de là, c'est l'attribut de téléchargement standard.

async download(remoteFilePath){
  const a = document.createElement('a')
  const dlURL = `/api/download?url=${encodeURIComponent(remoteFilePath)}`
  const blob = await fetch(dlURL).then(res => res.blob())
  a.href = URL.createObjectURL(blob)
  a.setAttribute('download', 'cool.mp4')
  document.body.appendChild(a)
  a.click()
  a.remove()
}
0
posit labs