An ode to Cross-origin image downloads
Tags: javascript, webdev, html, aws
Published: 2020-03-20 14:01:11

---
title: An ode to Cross-origin image downloads
published: true
description: Describing Cross-origin image downloads
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/16s6796ygobcqj5mp9za.jpg
tags: #javascript #webdev #html #aws
---
## 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:
```html
<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](https://www.chromestatus.com/feature/4969697975992320).

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](https://weblog.west-wind.com/posts/2007/may/21/downloading-a-file-with-a-save-as-dialog-in-aspnet) 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](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image), but I needed a download. The following StackOverflow [post](https://stackoverflow.com/questions/49474775/chrome-65-blocks-cross-origin-a-download-client-side-workaround-to-force-down?answertab=votes#tab-top) 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](https://javascript.info/fetch-crossorigin). 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](https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html) but you might also be able to use a trick with cors proxies like [cors-anywhere](https://cors-anywhere.herokuapp.com/).

I hope these links helped others who end up having similar issues.

![plusone](https://thepracticaldev.s3.amazonaws.com/i/ypvzafveynm5q1njmrs3.gif)

<sub><sup>Cover image base Designed by katemangostar / Freepik"</sup></sub>