Written by Nathanaël Cherrier

Force download using JavaScript

Published in

Share this

twitter facebook

Forcing a file to be downloaded by the browser (user) seems hard because we do not have a native Javascript function to do that. I know why these functions don't exist but I still have to force download files in my apps! For example, when I put business logic in my frontend app.

One of my clients asked me to add a wonderful feature to his app: download Excel report files. I have to start the download with Javascript but it won't be as simple as a GET request.

In a GET request using XHR or the fetch API, the data flow is catched to be processed by Javascript. That's not what I wanted to do. The browser should understand that my file must be downloaded on the user's computer instead of filling a variable in the Javascript engine.

The trick I found is to create an anchor object using JavaScript but not render it in the DOM. It means this anchor only exists as a JavaScript variable (I'm kidding, it's a JavaScript constant).

function downloadFile(data, name = 'file', type = 'text/plain') {
  const anchor = document.createElement('a')
  anchor.href = window.URL.createObjectURL(new Blob([data], { type }))
  anchor.download = name
  anchor.click()
}

This function take the name, the type and the data of the file and starts the download on the user's computer. You can also notice that only the data is really needed for a classic plain text file.

We use the data variable to build a Blob and a local URL to this Blob. data can be a Blob, it's not an issue.

We use the download attribute to let the browser know that we want the Blob to be downloaded with the name contained into the download attribute.

Last but not least, we have to trigger the click event on the anchor to launch its regular behavior and let the browser do its job.

Improved version

I love the simplicity of the downloadFile function above but I have to improve it to handle more production cases.

function downloadFile(data, name = 'file', type = 'text/plain') {
  const { createElement } = document
  const { URL: { createObjectURL, revokeObjectURL }, setTimeout } = window

  const blob = new Blob([data], { type })
  const url = createObjectURL(blob)

  const anchor = createElement('a')
  anchor.setAttribute('href', url)
  anchor.setAttribute('download', name)
  anchor.click()
  
  setTimeout(() => { revokeObjectURL(url) }, 100)
}

This function was tested using the last versions of the common browsers. Internet Explorer is not part of the browsers tested.

To help you if you must support Internet Explorer, here is what I know: the anchor trick won't work. You should try navigator.msSaveOrOpenBlob instead.

If your have any questions or advices, send me a tweet! Also if you like this post, don't forget to share it to your friends. You can also support the blog on Patreon.