Preface
So I'm developing this app, and my client is like: "Can I have just a download button for the images pls?" And all hell broke loose.
Longer preface
Not long ago I started working on a resource sharing application for a client of mine. They're a design studio, and they wanted to have an app where they can distribute their work/images/source files, etc to their clients. The idea was to have a system where you can create and manage artifacts, users, user groups, and you're able to set up arbitrary access rights between them.
Basically what they needed was something that my designer colleague described as a "google drive on drugs".
So we made it. How? that's a different story.
Anyway. One day my client was like, "It's cool that we can look at the images and right-click download them, but can we have a download button as well?". I was like "sure should be fairly simple". It was not.
Intricacies of the anchor tag
The first solution I found was simply using an anchor tag.
So the anchor tag has this download
attribute. Our illustrious helper w3Schools describes it with a fairly simple example:
<a href="/images/myw3schoolsimage.jpg" download="w3logo">
As simple as it could be - so I thought, and gave it a go. But my browser did not give a damn about it and just displayed the image in the same tab. What I did not calculate with was that my images and all artifacts were served from an AWS S3 bucket.
Same origin policy problems
Turns out that in most browsers to be compliant with the same-origin policy, the download
attribute only works for same-origin URLs. Hence, it cannot be used to download resources served from a different origin. This is well described in this chrome feature update doc.
Anyway even if it's served from the same origin there can be problems, which need to be resolved using Content-Disposition
header. I'm not going to go into this but here's a great article about the topic.
Shortly: To inform the client that the content of the resource is not meant to be displayed, the server must include an additional header in the response. The
Content-Disposition
header is the right header for specifying this kind of information.
Looking for Workarounds
The first thing I started to look for is some kind of workaround. Cross-origin image usage is well described at the corresponding Mozilla page, but I needed a download. The following StackOverflow post looked like the best approach for me. The point is that :blob
and :data
are unaffected so we can use a workaround using these, then fetch and emulating the download click.
CORS Errors
After this, a new problem came up thanks to the Cross-Origin Resource Sharing
policy. You can read up on the whole thing here. Read it. Seriously it's great and funny. I'm just putting here one quote:
"JavaScript also did not have any special methods to perform network requests at that time. It was a toy language to decorate a web page."
All I needed was to set up CORS on the AWS side like this but you might also be able to use a trick with cors proxies like cors-anywhere.
I hope these links helped others who end up having similar issues.
Cover image base Designed by katemangostar / Freepik"
Top comments (2)
Thank you! Very informative. I didn't know about the
download
attribute and its implications.Thanks! I just spent a few hours working through this issue and finally came to the same conclusion. Can't believe simply adding a download image button is this hard when there is a "Save image as.." option available on every single image on the web.