Skip to content

Saving a remote file

Daniel Niederberger edited this page Jan 22, 2019 · 15 revisions

FileSaver is built for saving client side generated content, but if the content is coming from a server then there is different ways to achieve the goal of saving the file downloaded from the cloud.

Using Http Header

Content-Disposition attachment header is the best preferred way to download files from the browser. It has better cross browser compatible, won't have any memory limit and it doesn't require any JavaScript.

Content-Disposition: attachment; filename="filename.jpg" filename*="filename.jpg"

Directives

filename

Is followed by a string containing the original name of the file transmitted. The filename is always optional and must not be used blindly by the application: path information should be stripped, and conversion to the server file system rules should be done. This parameter provides mostly indicative information. When used in combination with Content-Disposition: attachment, it is used as the default filename for an eventual 'Save As" dialog presented to the user.

filename*

The parameters "filename" and "filename*" differ only in that "filename*" uses the encoding defined in RFC 5987. When both "filename" and "filename*" are present in a single header field value, "filename*" is preferred over "filename" when both are present and understood.

Using a form element (Other then GET methods)

If the file is generated using a POST request you may think you would need to use ajax to complete the request and then download the result that you have buffered up in the memory while creating your Blob.
That is a waste of time and memory. You can accomplish it with a regular <form> submission and then respond with a content-disposition attachment header. Ajax/JS isn't the solution to everything. It would be better to create a hidden form + fields and submit it using javascript then using ajax.

Using a[download]

If you have no way to add the content-disposition attachment header a better way to save it directly to the hard drive would be to use the download attribute

<a href="uploads/screenshot.png" download="cat.png">download cat.png</a>

This attribute instructs browsers to download a URL instead of navigating to it, so the user will be prompted to save it as a local file. If the attribute has a value, it is used as the pre-filled file name in the Save prompt (the user can still change the file name if they want). There are no restrictions on allowed values, though / and \ are converted to underscores. Most file systems limit some punctuation in file names, and browsers will adjust the suggested name accordingly.

Notes:

  • This attribute can be used with blob: URLs and data: URLs to download content generated by JavaScript, such as pictures created in an image-editor Web app.
  • If the HTTP header Content-Disposition: gives a different filename than this attribute, the HTTP header takes priority over this attribute.
  • If Content-Disposition: is set to inline, Firefox prioritizes Content-Disposition, like the filename case, while Chrome prioritizes the download attribute.

Using Ajax + FileSaver

background

If for some reason you need some headers (like authentication) for downloading the remote file. Then ajax will probably be the only way forward. Same goes if download attribute isn't supported in the browser you are targeting or content-disposition could not be added from the back-end server.

One way you could add request headers and modify the response header to include the content-disposition header is through Service Worker but I guess nearly newbody would go that route. Unless you use StreamSaver.js which is the core method of saving large files as a stream. but i won't go in to that, it requires the page to be https and have support for service workers.

The important thing in all request is to get the response as a blob. If you try to get the content they way you usually dose with text responses. This tells the browser not to parse the text content, and to let the bytes pass through unprocessed. Creating a blob from textContent is problematic if downloading binary data

Diffrent ajax methods:

Plain vanilla XMLHttpRequest
var xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.responseType = 'blob'
xhr.onload = function() {
  FileSaver.saveAs(xhr.response, filename);
}
xhr.send()
Fetch API
// ES7
const res = await fetch(url)
const blob = await res.blob()
saveAs(blob, fileName)

// ES6
fetch(url)
  .then(res => res.blob())
  .then(blob => saveAs(blob, fileName))

// ES5
fetch(url)
  .then(function(res) {
    return res.blob()
  })
  .then(function(blob) {
    saveAs(blob, fileName)
  })
Angulars $http
$http({
  url: "http://localhost:8080/filename.zip",
  responseType: "blob"
}).then(function(response) {
  saveAs(response.data, fileName)
})
jQuery's $.ajax()

Don't! They don't support changing the response type.

Clone this wiki locally