DEV Community

loading...
Cover image for How to force the browser to download remote resource

How to force the browser to download remote resource

dlw profile image Darren White Originally published at darrenwhite.dev ・3 min read

In a recent project, I needed to offer the ability to download files. Until recently I wasn't aware of the HTMLAnchorElement.download property. I'll explain how to use the download property and why this wouldn't work for my situation.

First though, here is a link to the repository and demo:

Using the HTML5 download property

By default, anchor elements will navigate to the href element. You can add the download property which will prompt the browser to download the file:

<a download href="https://darrenwhite.dev/images/john-fowler-d2YMQ-hZ3og-unsplash.jpg">
  Download image
</a>
Enter fullscreen mode Exit fullscreen mode

However, images and also Firefox only allows users to download files of the same origin. As the files for my project would be served from a different origin I would need a way to force the download.

Using fetch to download

My solution was to use fetch to get the remote resource:

const file = await fetch(*URL_TO_REMOTE_RESOURCE*);

const blob = await file.blob();
const url = URL.createObjectURL(blob);
Enter fullscreen mode Exit fullscreen mode

Once the resource is downloaded, create an anchor element with the download and trigger the click event:

const downloadLink = document.createElement("a");

downloadLink.href = linkSource;
downloadLink.download = [download name];
downloadLink.click();
Enter fullscreen mode Exit fullscreen mode

Here's the complete code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>dlw download as data demo</title>
    <link rel="stylesheet" href="./build/tailwind.css" />
  </head>
  <body class="bg-gray-900 h-full">
    <main class="min-h-screen flex flex-col justify-center py-12 sm:px-6 lg:px-8">
      <div class="max-w-3xl mx-auto flex flex-col items-center space-y-2">
        <div class="aspect-w-3 aspect-h-2 w-96">
          <img 
            class="object-fill shadow-lg rounded-lg"
            src="https://darrenwhite.dev/images/john-fowler-d2YMQ-hZ3og-unsplash.jpg"
            alt="Star shot in Cathedral Valley State Park, Nevada"
          >
        </div>
        <a
          href="https://darrenwhite.dev/images/john-fowler-d2YMQ-hZ3og-unsplash.jpg"
          class="inline-flex items-center mt-4 px-6 py-3 border border-transparent shadow-sm text-base font-medium rounded-md text-white bg-pink-600 hover:bg-pink-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-pink-500"
          data-remote
        >
          Remote
          <svg
            class="ml-2 w-6 h-6"
            fill="currentColor"
            viewBox="0 0 20 20"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path
              fill-rule="evenodd"
              d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm3.293-7.707a1 1 0 011.414 0L9 10.586V3a1 1 0 112 0v7.586l1.293-1.293a1 1 0 111.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
              clip-rule="evenodd"
            ></path>
          </svg>
        </a>
      <div>
    </main>

    <script defer type="text/javascript">
      const a = document.querySelector('a[data-remote]')

      a.addEventListener('click', async (e) => {
        e.preventDefault()

        const file = await fetch(e.target.href);

        const blob = await file.blob();

        const blobUrl = URL.createObjectURL(blob);

        const downloadLink = document.createElement("a");
        downloadLink.href = blobUrl;
        downloadLink.download = 'download-example.jpg';

        downloadLink.click();
      })
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Discussion (0)

pic
Editor guide