Force Download with JavaScript

By  on  

Force download scripts have been an important part of internet usability for a long time.  I can attest to that by the number of times I've implemented this feature on the server side and the popularity of my PHP Force Download post, even to this day.  With the web world having moved much more the client side, I started looking for a method to force download without the need of a server, and I found it....right in the Firefox DevTools Debugger!

The JavaScript

The function to do this is quite small and relies on URL.createObjectUrl:

function downloadFile(data, fileName, type="text/plain") {
  // Create an invisible A element
  const a = document.createElement("a");
  a.style.display = "none";
  document.body.appendChild(a);

  // Set the HREF to a Blob representation of the data to be downloaded
  a.href = window.URL.createObjectURL(
    new Blob([data], { type })
  );

  // Use download attribute to set set desired file name
  a.setAttribute("download", fileName);

  // Trigger the download by simulating click
  a.click();

  // Cleanup
  window.URL.revokeObjectURL(a.href);
  document.body.removeChild(a);
}

The function injects an <a> element into the body, sets it URL to a Blob value to the text content of the destination file, and clicks the element to trigger the download.  The element remains hidden during the process and is removed from the DOM immediately after the click() call.  As soon as the function is called, the browser's download prompt is displayed.

I look forward to learning more about both createObjectURL and Blob; those two are the true magic of this technique!

Shout out to Sneha Jain for implementing this great technique within the Firefox DevTools debugger!

Recent Features

  • By
    Create a CSS Cube

    CSS cubes really showcase what CSS has become over the years, evolving from simple color and dimension directives to a language capable of creating deep, creative visuals.  Add animation and you've got something really neat.  Unfortunately each CSS cube tutorial I've read is a bit...

  • By
    Regular Expressions for the Rest of Us

    Sooner or later you'll run across a regular expression. With their cryptic syntax, confusing documentation and massive learning curve, most developers settle for copying and pasting them from StackOverflow and hoping they work. But what if you could decode regular expressions and harness their power? In...

Incredible Demos

  • By
    HTML5&#8217;s window.postMessage API

    One of the little known HTML5 APIs is the window.postMessage API.  window.postMessage allows for sending data messages between two windows/frames across domains.  Essentially window.postMessage acts as cross-domain AJAX without the server shims. Let's take a look at how window.postMessage works and how you...

  • By
    Using MooTools For Opacity

    Although it's possible to achieve opacity using CSS, the hacks involved aren't pretty. If you're using the MooTools JavaScript library, opacity is as easy as using an element's "set" method. The following MooTools snippet takes every image with the "opacity" class and sets...

Discussion

  1. Pauli Sudarshan Terho

    Typo {type} should be replaced by {type: type}. Ex: {type: "text/plain;charset=utf-8"}

    • { type } is acting exactly like { type : type}

  2. Bronius

    Quite nice and concise. This works with generating a text file and downloading it: What’s the magic to fetch a remote text file not even on my server and handing it back to the browser for download? I want to avoid streaming through php.. In my case they are small enough that maybe I could stream thru js?

    • Bronius

      Ah ha! It is possible! Preceding Dave Walsh’s script above, I first stream a fetch of the file (remote or “local” on the server) with fetch() like:

              let blob = await fetch(uri).then(r => r.blob());
              let fileContents = await (new Response(blob)).text();
      

      Then I pass fileContents into the function in this solution as data. I don’t know if this is the best way, but it seems to be working great in my testing.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!