Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prompt file download with XMLHttpRequest

I'm aware that jQuery's ajax method cannot handle downloads, and I do not want to add a jQuery plugin to do this.

I want to know how to send POST data with XMLHttpRequest to download a file.

Here's what I've tried:

var postData = new FormData(); postData.append('cells', JSON.stringify(output));  var xhr = new XMLHttpRequest(); xhr.open('POST', '/export/', true); xhr.setRequestHeader("X-CSRFToken", csrftoken); xhr.responseType = 'arraybuffer'; xhr.onload = function (e) {     console.log(e);     console.log(xhr); } xhr.send(postData); 

I'm working with Django, and the file appears to be sending back to the client successfully. In the network tab in Chrome, I can see gibberish in the preview tab (which I expect). But I want to send back a zip file, not a text representation of the zip file. Here's the Django back end:

wrapper = FileWrapper(tmp_file) response = HttpResponse(wrapper, content_type='application/zip') response['Content-Disposition'] = "attachment; filename=export.zip" response['Content-Length'] = tmp_file.tell() return response 

I've searched this for hours now without finding a proper example on how to do this with XMLHttpRequests. I don't want to create a proper html form with a POST action because the form data is rather large, and dynamically created.

Is there something wrong with the above code? Something I'm missing? I just don't know how to actually send the data to the client as a download.

like image 928
bozdoz Avatar asked Mar 28 '14 22:03

bozdoz


People also ask

Can we download file using AJAX?

You need to set it according to your file type. You can use this technique to download any kind of files. "We cannot download the file through Ajax, must use XMLHttpRequest". XMLHttpRequest is AJAX by definition.

How do I get HTML from XMLHttpRequest?

Retrieving an HTML resource as a DOM using XMLHttpRequest works just like retrieving an XML resource as a DOM using XMLHttpRequest , except you can't use the synchronous mode and you have to explicitly request a document by assigning the string "document" to the responseType property of the XMLHttpRequest object after ...

Is it safe to use XMLHttpRequest?

Both are equally insecure if you make the requests over unsecured connections. Both are equally secure if you make the requests over SSL connections. A request made over an SSL connection might (ultimately) be profoundly non-secure if you aren't protecting the data appropriately on the server, etc.


2 Answers

If you set the XMLHttpRequest.responseType property to 'blob' before sending the request, then when you get the response back, it will be represented as a blob. You can then save the blob to a temporary file and navigate to it.

var postData = new FormData(); postData.append('cells', JSON.stringify(output));  var xhr = new XMLHttpRequest(); xhr.open('POST', '/export/', true); xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.responseType = 'blob'; xhr.onload = function (e) {     var blob = e.currentTarget.response;     var contentDispo = e.currentTarget.getResponseHeader('Content-Disposition');     // https://stackoverflow.com/a/23054920/     var fileName = contentDispo.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1];     saveOrOpenBlob(blob, fileName); } xhr.send(postData); 

And here's an example implementation of saveOrOpenBlob:

function saveOrOpenBlob(blob, fileName) {     window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;     window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fs) {         fs.root.getFile(fileName, { create: true }, function (fileEntry) {             fileEntry.createWriter(function (fileWriter) {                 fileWriter.addEventListener("writeend", function () {                     window.location = fileEntry.toURL();                 }, false);                 fileWriter.write(blob, "_blank");             }, function () { });         }, function () { });     }, function () { }); } 

If you don't care about having the browser navigate to the file when it's a viewable file type, then making a method that always saves directly to file is much simpler:

function saveBlob(blob, fileName) {     var a = document.createElement('a');     a.href = window.URL.createObjectURL(blob);     a.download = fileName;     a.dispatchEvent(new MouseEvent('click')); } 
like image 77
Steven Doggart Avatar answered Sep 28 '22 15:09

Steven Doggart


UPDATE: this answer is not accurate anymore since the introduction of Blob API. Please refer to Steven's answer for details.


ORIGINAL ANSWER:

XHR request will not trigger file download. I can't find explicit requirement, but W3C doc on XMLHttpRequest doesn't describe any special reaction on content-disposition=attachment responses either

You could download file by window.open() in separate tab, if it was not POST request. Here it was suggested to use a hidden form with target=_blank

like image 41
Marat Avatar answered Sep 28 '22 14:09

Marat