href tag download attribute - how to make it force download of externally hosted image?

白昼怎懂夜的黑 提交于 2021-02-08 13:57:20

问题


I want to make a <a href link which, when clicked, forces your browser to open a "Save As" dialogue. The use of the HTML5 download attribute should have this behavior on all modern browsers.

When the target is an externally hosted image file, this does not work. The <a href link will navigate to the image instead of downloading it. (Tested using images hosted on Imgur and Tumblr)

<a href="https://i.stack.imgur.com/440u9.png" download>
  <img src="https://i.stack.imgur.com/440u9.png" width="200"/>
</a>

Similar HTML code does work if the image is hosted locally on your server, for example

<a href="440u9.png" download>
  <img src="440u9.png" width="200"/>
</a>

Another example - this will work in the W3Schools Tryit Editor, but will not work if you copy the HTML to another sandbox like JSFiddle

<a href="https://www.w3schools.com/images/myw3schoolsimage.jpg" download>
  <img src="https://www.w3schools.com/images/myw3schoolsimage.jpg" width="104" height="142">
</a>


Possible causes explored:

  • Bad image URL: no, the URL is good, you can open the URL as a plain image in your browser
  • Links with https: can't be used this way due to security: no, a URL with https: is good if the URL points to the same domain, tested in W3Schools Tryit Editor
  • Server-side redirect: an http://... link can be redirected to https://... so maybe that could be a cause of failure, but the https://... link also does not work
  • Suppression of download attribute on server by specific hosts? is that even possible? the download attribute should control only client-side browser action surely?

Any workarounds? I have tried Javascript download methods discussed here and here but these all eventually involved a browser action similar to clicking on an href link.

Excellent in-depth discussion of download links including application/force-download setting in PHP on the server

Related StackOverflow question: Force external download url (answer seems to be incorrect)


回答1:


Turns out the answer is that this cannot be done in the way I was asking. Modern browsers prevent cross-domain downloads as a security feature.

See: Making Firefox and Chrome download image under a specific name

Force <a download /> to download image instead of opening url link to image

If the download must be byte-identical to the original file, the only solutions I can find right now are:

  1. The server must first use an httpClient request to get the target resource, save it to disk locally on the server, then provide an <a href='tmp.jpg' download> link with the URI being that saved file. Or, some server frameworks are able to create a bytestream simulating file access, in which case the resource could be cached to memory instead of saving to disk.

    Drawbacks: data cost equal to twice the filesize (once in, once out), roundtrip delay, use of server memory/filesystem. Probably every possible file with a download link has to be stored locally on the server before the page is served (or maybe it can be done asynchronously using websockets, serving the file after the user clicks the download link, but with a delay). For large files or high usage servers these are serious drawbacks.

  2. Open the target URL in a new tab, and let the client switch to that tab, and manually right-click and Save As...

    Drawbacks: this is exactly what we are trying to avoid!

I'm still wondering if there is some hack for this, like giving a local URL and having the server redirect a request for that particular resource to the external site.




回答2:


My workaround: download an image to canvas, then download canvas image to the local drive.

The code:

    <canvas id="downloadCanvas"></canvas>

    <script>
        var strDataURI = "https://78.media.tumblr.com/33f1b72778ca7352979b0f4555f08f02/tumblr_pfega8eUyH1r68v93o1_640.jpg";
        var img = new Image;
        img.crossOrigin = "Anonymous";
        img.src = strDataURI;


        var canvas = document.getElementById('downloadCanvas');
        var ctx = canvas.getContext('2d');

        img.onload = function(){

          canvas.width = img.width;
          canvas.height = img.height;
          ctx.drawImage(img,0,0);

          var dwBtn = document.getElementById("dw0");
            var dwLink = canvas.toDataURL('image/jpeg', 1.0);

          download(dwLink, "image.jpeg");
        };


    function download(dataurl, filename) {
      var a = document.createElement("a");
      a.href = dataurl;
      a.setAttribute("download", filename);
      var b = document.createEvent("MouseEvents");
      b.initEvent("click", false, true);
      a.dispatchEvent(b);
      return false;
    }

</script>

As the OP asked, he needs captured image to have same extension. (pseudoish code)

Change these lines:

  var ext = ExtensionFromUrl(url);
  var fileName = NameFromUrl(url);

      var dwLink = canvas.toDataURL('image/'+ext, 1.0);
      download(dwLink, fileName+"."+ext);

Note that: This answer won't work with GIF images. Also, as they are captured images, transparency channels will be lost!



来源:https://stackoverflow.com/questions/52465847/href-tag-download-attribute-how-to-make-it-force-download-of-externally-hosted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!