Force download using JavaScript
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.