How to receive files in Javascript sent from Flask using send_file() - javascript

I am developing a web application using HTML + plain Javascript in the frontend, and Flask as backend. The application sends some ID to the server, the server should generate a report as PDF file and send it back to the client.
I am using Flask for the backend and I have created the following endpoint:
#app.route("/download_pdf", methods=['POST'])
def download_pdf():
if request.method == 'POST':
report_id = request.form['reportid']
print(report_id) //Testing purposes.
// do some stuff with report_id and generate a pdf file.
return send_file('/home/user/report.pdf', mimetype='application/pdf', as_attachment=True)
// I already tried different values for mimetype and as_attachment=False
From the command line I can test the endpoint and I get the right file, and the server console prints the 123 report_id as expected:
curl --form reportid=123 http://localhost:5000/download_pdf >> output.pdf
For the frontend side I created a button that calls a Javascript function:
<button id=pdf_button onclick="generatePdf()"> PDF </button>
The Javascript function looks like this:
function generatePdf(){
var report_list = document.getElementById("report_list")
if (report_list.selectedIndex < 0){
alert("Please, select a report.");
}else{
var req = new XMLHttpRequest();
req.open("POST", "/download_pdf", true);
req.responseType = "document";
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onreadystatechange = function(){
console.log(req.readyState)
console.log(req.status)
console.log(req.response)
var link = document.createElement('a')
link.href = req.response;
link.download="report.pdf"
link.click()
}
var selected_value = report_list.options[report_list.selectedIndex].value;
var params="reportid="+selected_value;
req.send(params);
}
};
req.response is null in this case. However, the call to the endpoint has been done correctly, as the backend console prints the report_id as expected.
Already tried:
Using "blob" and "arraybuffer" as responseType as in https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Receiving_binary_data_using_JavaScript_typed_arrays
Checking for the HTTP return code, which is always 0.
Calling req.onload instead of req.onreadystatechange. The console shows nothing.
Lastly, the Firefox console shows these 6 messages after pressing the related button (please, observe the console.log() calls in the previous code):
2
0
null
4
0
null
It seems that the Javascript function has been called twice when the button is pressed.
My goal is to get the PDF downloaded. I don't know if what am I doing wrong; I'd thank any help on this.

Finally, I found what the problem was and I post this for the record.
I thought it was unrelated, but the <button> calling the Javascript function was inside a <form>. I checked that the form was updated before the call to the endpoint finished, causing the call to finish prepaturely.
If somebody else needs this as example, a snipet of the final code is as follows:
HTML (both the select and button are not part of a <form>):
<select id="report_list" size=20> ... </select>
...
<button id="pdf_button" onclick="generatePdf()"> PDF </button>
Javascript:
function generatePdf(){
var report_list = document.getElementById("report_list");
var req = XMLHttpRequest();
var selected_value = report_list.options[report_list.selectedIndex].value;
req.open("POST", "/reports/"+selected_value+"/pdf", true);
req.responseType = "blob";
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
var blob = new Blob([this.response], {type: "application/pdf"});
var url = window.URL.createObjectURL(blob);
var link = document.createElement('a');
document.body.appendChild(link);
link.style = "display: none";
link.href = url;
link.download = "report.pdf";
link.click();
setTimeout(() => {
window.URL.revokeObjectURL(url);
link.remove(); } , 100);
}
};
req.send();
}
Flask:
#app.route("/reports/<id>/pdf", methods=['POST'])
def get_pdf(id):
if request.method == 'POST':
return send_file(get_pdf_path(id), mimetype='application/pdf')
I am not sure if this is the best or more elegant way to get this done, but so far it works for me.

Your ajax settings are wrong, they should be like these
req.open("POST", "/download_pdf", true);
req.responseType = "blob";
req.onreadystatechange = function() {
console.log(req.readyState)
console.log(req.status)
const blob = new Blob([req.response]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a')
link.href = url
link.download = "report.pdf"
link.click()
}
The response type should be blob and when you get the response, parse it as a blob. After some time, remove the link
setTimeout(() => {
window.URL.revokeObjectURL(url);
link.remove();
}, 100);

Related

How to serve file from REST api backend to frontend [duplicate]

I want to send an "ajax download request" when I click on a button, so I tried in this way:
javascript:
var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();
download.php:
<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");
readfile("file.txt");
?>
but doesn't work as expected, how can I do ? Thank you in advance
Update April 27, 2015
Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.
You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.
Original answer
You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.
$.ajax({
url: 'download.php',
type: 'POST',
success: function() {
window.location = 'download.php';
}
});
Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.
To make the browser downloads a file you need to make the request like that:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:
window.location = 'download.php';
The browser should recognise the binary download and not load the actual page but just serve the file as a download.
Cross browser solution, tested on Chrome, Firefox, Edge, IE11.
In the DOM, add an hidden link tag:
<a id="target" style="display: none"></a>
Then:
var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)
req.onload = function (event) {
var blob = req.response;
var fileName = null;
var contentType = req.getResponseHeader("content-type");
// IE/EDGE seems not returning some response header
if (req.getResponseHeader("content-disposition")) {
var contentDisposition = req.getResponseHeader("content-disposition");
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
} else {
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
} else {
var el = document.getElementById("target");
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
}
};
req.send();
It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.
I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:
window.location="download.php?filename=export.csv";
My download.php file looks like this:
<?php
$file = $_GET['filename'];
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$file."");
header("Content-Transfer-Encoding: binary");
header("Content-Type: binary/octet-stream");
readfile($file);
?>
There is no page refresh whatsoever and the file automatically starts downloading.
NOTE - Tested in the following browsers:
Chrome v37.0.2062.120
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
I prefer location.assign(url);
Complete syntax example:
document.location.assign('https://www.urltodocument.com/document.pdf');
developer.mozilla.org/en-US/docs/Web/API/Location.assign
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a spreadsheet file. It is easily done with the following code.
fetch(url, {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
})
I believe this approach to be much easier to understand than other XMLHttpRequest solutions. Also, it has a similar syntax to the jQuery approach, without the need to add any additional libraries.
Of course, I would advise checking to which browser you are developing, since this new approach won't work on IE. You can find the full browser compatibility list on the following link.
Important: In this example I am sending a JSON request to a server listening on the given url. This url must be set, on my example I am assuming you know this part. Also, consider the headers needed for your request to work. Since I am sending a JSON, I must add the Content-Type header and set it to application/json; charset=utf-8, as to let the server know the type of request it will receive.
#Joao Marcos solution works for me but I had to modify the code to make it work on IE, below if what the code looks like
downloadFile(url,filename) {
var that = this;
const extension = url.split('/').pop().split('?')[0].split('.').pop();
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "blob";
req.onload = function (event) {
const fileName = `${filename}.${extension}`;
const blob = req.response;
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
};
req.send();
},
Decoding a filename from the header is a little bit more complex...
var filename = "default.pdf";
var disposition = req.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1)
{
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/['"]/g, '');
}
This solution is not very different from those above, but for me it works very well and i think it's clean.
I suggest to base64 encode the file server side (base64_encode(), if you are using PHP) and send the base64 encoded data to the client
On the client you do this:
let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
let uri = URL.createObjectURL(blob);
let link = document.createElement("a");
link.download = THE_FILE_NAME,
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
This code puts the encoded data in a link and simulates a click on the link, then it removes it.
Your needs are covered by
window.location('download.php');
But I think that you need to pass the file to be downloaded, not always download the same file, and that's why you are using a request, one option is to create a php file as simple as showfile.php and do a request like
var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);
showfile.php
<?php
$file = $_GET["file"]
echo $file;
where file is the file name passed via Get or Post in the request and then catch the response in a function simply
if(ajaxRequest.readyState == 4){
var file = ajaxRequest.responseText;
window.location = 'downfile.php?file=' + file;
}
}
there is another solution to download a web page in ajax. But I am referring to a page that must first be processed and then downloaded.
First you need to separate the page processing from the results download.
1) Only the page calculations are made in the ajax call.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
I hope this solution can be useful for many, as it was for me.
this works for me
var dataObj = {
somekey:"someValue"
}
$.ajax({
method: "POST",
url: "/someController/someMethod",
data: dataObj,
success: function (response) {
const blob = new Blob([response], { type: 'text/csv' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.csv";
document.body.appendChild(a);
a.click();
}
});

How to download file with post method? [duplicate]

I want to send an "ajax download request" when I click on a button, so I tried in this way:
javascript:
var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();
download.php:
<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");
readfile("file.txt");
?>
but doesn't work as expected, how can I do ? Thank you in advance
Update April 27, 2015
Up and coming to the HTML5 scene is the download attribute. It's supported in Firefox and Chrome, and soon to come to IE11. Depending on your needs, you could use it instead of an AJAX request (or using window.location) so long as the file you want to download is on the same origin as your site.
You could always make the AJAX request/window.location a fallback by using some JavaScript to test if download is supported and if not, switching it to call window.location.
Original answer
You can't have an AJAX request open the download prompt since you physically have to navigate to the file to prompt for download. Instead, you could use a success function to navigate to download.php. This will open the download prompt but won't change the current page.
$.ajax({
url: 'download.php',
type: 'POST',
success: function() {
window.location = 'download.php';
}
});
Even though this answers the question, it's better to just use window.location and avoid the AJAX request entirely.
To make the browser downloads a file you need to make the request like that:
function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};
req.send();
}
You actually don't need ajax at all for this. If you just set "download.php" as the href on the button, or, if it's not a link use:
window.location = 'download.php';
The browser should recognise the binary download and not load the actual page but just serve the file as a download.
Cross browser solution, tested on Chrome, Firefox, Edge, IE11.
In the DOM, add an hidden link tag:
<a id="target" style="display: none"></a>
Then:
var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)
req.onload = function (event) {
var blob = req.response;
var fileName = null;
var contentType = req.getResponseHeader("content-type");
// IE/EDGE seems not returning some response header
if (req.getResponseHeader("content-disposition")) {
var contentDisposition = req.getResponseHeader("content-disposition");
fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
} else {
fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
} else {
var el = document.getElementById("target");
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
}
};
req.send();
It is possible. You can have the download started from inside an ajax function, for example, just after the .csv file is created.
I have an ajax function that exports a database of contacts to a .csv file, and just after it finishes, it automatically starts the .csv file download. So, after I get the responseText and everything is Ok, I redirect browser like this:
window.location="download.php?filename=export.csv";
My download.php file looks like this:
<?php
$file = $_GET['filename'];
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=".$file."");
header("Content-Transfer-Encoding: binary");
header("Content-Type: binary/octet-stream");
readfile($file);
?>
There is no page refresh whatsoever and the file automatically starts downloading.
NOTE - Tested in the following browsers:
Chrome v37.0.2062.120
Firefox v32.0.1
Opera v12.17
Internet Explorer v11
I prefer location.assign(url);
Complete syntax example:
document.location.assign('https://www.urltodocument.com/document.pdf');
developer.mozilla.org/en-US/docs/Web/API/Location.assign
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a spreadsheet file. It is easily done with the following code.
fetch(url, {
body: JSON.stringify(data),
method: 'POST',
headers: {
'Content-Type': 'application/json; charset=utf-8'
},
})
.then(response => response.blob())
.then(response => {
const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.xlsx";
document.body.appendChild(a);
a.click();
})
I believe this approach to be much easier to understand than other XMLHttpRequest solutions. Also, it has a similar syntax to the jQuery approach, without the need to add any additional libraries.
Of course, I would advise checking to which browser you are developing, since this new approach won't work on IE. You can find the full browser compatibility list on the following link.
Important: In this example I am sending a JSON request to a server listening on the given url. This url must be set, on my example I am assuming you know this part. Also, consider the headers needed for your request to work. Since I am sending a JSON, I must add the Content-Type header and set it to application/json; charset=utf-8, as to let the server know the type of request it will receive.
#Joao Marcos solution works for me but I had to modify the code to make it work on IE, below if what the code looks like
downloadFile(url,filename) {
var that = this;
const extension = url.split('/').pop().split('?')[0].split('.').pop();
var req = new XMLHttpRequest();
req.open("GET", url, true);
req.responseType = "blob";
req.onload = function (event) {
const fileName = `${filename}.${extension}`;
const blob = req.response;
if (window.navigator.msSaveBlob) { // IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
URL.revokeObjectURL(link.href);
};
req.send();
},
Decoding a filename from the header is a little bit more complex...
var filename = "default.pdf";
var disposition = req.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1)
{
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1])
filename = matches[1].replace(/['"]/g, '');
}
This solution is not very different from those above, but for me it works very well and i think it's clean.
I suggest to base64 encode the file server side (base64_encode(), if you are using PHP) and send the base64 encoded data to the client
On the client you do this:
let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
let uri = URL.createObjectURL(blob);
let link = document.createElement("a");
link.download = THE_FILE_NAME,
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
This code puts the encoded data in a link and simulates a click on the link, then it removes it.
Your needs are covered by
window.location('download.php');
But I think that you need to pass the file to be downloaded, not always download the same file, and that's why you are using a request, one option is to create a php file as simple as showfile.php and do a request like
var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);
showfile.php
<?php
$file = $_GET["file"]
echo $file;
where file is the file name passed via Get or Post in the request and then catch the response in a function simply
if(ajaxRequest.readyState == 4){
var file = ajaxRequest.responseText;
window.location = 'downfile.php?file=' + file;
}
}
there is another solution to download a web page in ajax. But I am referring to a page that must first be processed and then downloaded.
First you need to separate the page processing from the results download.
1) Only the page calculations are made in the ajax call.
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
I hope this solution can be useful for many, as it was for me.
this works for me
var dataObj = {
somekey:"someValue"
}
$.ajax({
method: "POST",
url: "/someController/someMethod",
data: dataObj,
success: function (response) {
const blob = new Blob([response], { type: 'text/csv' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.csv";
document.body.appendChild(a);
a.click();
}
});

ajax -> save as dialog before downloading

I am programming an embedded Device in C with a webserver. One Task is to download files from this devices. I want to Download serveral files at once, so i created an ajax-request, which using POST-Request and a bunch of filenames to return a zip-file (i create these zip-file on my own on the device). Everything works fine, but the dialog save as appears after the whole zip-file was transmitted.
At server-side the device is sending the 200 OK-, Content-Type: application/octet-stream- and Content-Disposition: attachment; filename="testzip.zip"-headers.
At client-side i using this javascript-code(got this from stackoverlfow: Handle file download from ajax post):
function downloadFiles(filenames) {
var xhr = new XMLHttpRequest();
xhr.open('POST', /file-save/, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
if (this.status === 200) {
var filename = "test.zip";
var type = xhr.getResponseHeader('Content-Type');
var blob = new Blob([this.response], { type: type });
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
// use HTML5 a[download] attribute to specify filename
var a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
}
};
xhr.send(filenames);
}
The if-statement if (this.status === 200) is reached, when the whole file is transmitted. If the size of the file is small, there is not a problem, because the user isn't recognizing the lack of time. But is the file about 50MB the user can't see any download although the file is downloading. In my opinion the reason is a.click(), because the click-method imitades the begin of the download.
Is there sombody who can help me out with a solution or some hints?
By the way, jquery isn't an option!.
Thanks for any help
EDIT: my goal is to download a file like on every webpage with large files, where i get a dialog with the location to save and i can see the download-progress.
SOLUTION(Hint from Herr Derb):
function downloadFiles(filenames) {
var xhr = new XMLHttpRequest();
xhr.open('POST', /file_save/, true);
xhr.onload = function () {
if (this.status === 200) {
var mydisp = xhr.getResponseHeader('Content-Disposition');
var save_response = xhr.responseText;
var var_json_format = JSON.parse(save_response);
/* check for errors */
if(var_json_format["error"]) {
return;
} else {
status = _.findWhere(var_json_format["link"], {id : 'status'}).value;
download_id = _.findWhere(var_json_format["link"], {id : 'download_id'}).value;
}
if(status != "active") {
return;
}
var filename = "test.zip";
var downloadUrl = "/file_save/" + download_id;
var a = document.createElement("a");
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // cleanup
}
};
xhr.send(filenames);
return;
}
Your first request should only create the zip file on your server and return a link to reach it. After you received that link on the client site, simply execute it. This way, everything will happen as you desire, as it will be a regular file download.
And ss soon the download is finished, your free to delete the file again.

How to make browser download file from xhr request

I'm sending xhr request to server to download a file. I'm including authorization token into the request so I can't download a file without using xhr. What steps should I take to make browser download a file when response from the server is received? And what headers should the server include?
This is a piece of code that works for me. Im using it for testing so its not the cleanest way I guess. But it can show a picture.
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var downloadUrl = URL.createObjectURL(xhttp.response);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = downloadUrl;
a.download = "";
a.click();
}
};
xhttp.open("GET", fileUrl, true);
xhttp.responseType = "blob";
xhttp.setRequestHeader('Authorization', token);
xhttp.send();
This piece is not crucial, I was just need it in my case:
xhttp.setRequestHeader('Authorization', token);
This link can be usefull as well: Sending and Receiving Binary Data

Authenticated AJAX File Download

I'm integrating an intranet with a document management system. The DMS has a SOAP API. We've built a client that receives REST calls, makes the SOAP calls, and returns JSON or document data.
The problem is all of the solutions for AJAX filedownload seem to use iFrame (see John Culniver's filedownload plugin).
I can't use this because I need to provide authentication credentials in the header. The only other potential solution I can think of is using window.open (if I can get past browser popup blocking).
Does anyone have another potential solution or how might do this with window.open??
Thanks
I don't think there is a client-side solution for this problem. window.open isn't going to let you set the request headers. You'll need to do something like send a cookie or some other value to the server and add server-side code that alleviates the need for the request header.
See the answers to:
Offer a generated file for download from jQuery post
How to set a Header field on POST a form?
I was able to do it successfully. I'm my example I'm using Basic Authentication however you can replace your Authorization header with different value (e.g you can replace it with Bearer Yourtokenvalue.
Here is the code snippet
$(document).on("click", "#btn", function() {
var username = "yourUserNameForBasicAuthentication";
var password = "yourPasswordForBasicAuthentication"
console.log("button clicked");
var request = new XMLHttpRequest();
request.open("GET", $("#txtUrl").val().trim(), true);
//Set the authorization headers
request.setRequestHeader("Authorization", "Basic " + window.btoa(username + ":" + password));
//Set the Response type as blob which means raw data
request.responseType = "blob";
//We will initiate a default file name in case no file name is detected later on from headers of HTTP Request
var fileName = "unnamed.pdf";
request.onload = function(event) {
if (request.status == 200) {
console.log("success response received");
var blob = request.response;
//Getting contenttype from response headers
var contentType = request.getResponseHeader("content-type");
if (contentType == null) {
contentType = "application/pdf";
}
//Check 'Content-Disposition' header to get filename of file (if possible)
if (request.getResponseHeader("Content-Disposition")) {
var contentDisposition = request.getResponseHeader("Content-Disposition");
fileName = contentDisposition.substring(contentType.indexOf("=") + 1);
}
if (window.navigator.msSaveOrOpenBlob) {
// Internet Explorer
window.navigator.msSaveOrOpenBlob(new Blob([blob], {
type: contentType
}), fileName);
} else {
var el = document.createElement("a");
document.body.appendChild(el);
el.href = window.URL.createObjectURL(blob);
el.download = fileName;
el.click();
el.remove();
window.URL.revokeObjectURL(el.href);
}
} //end of Status code
else {
alert("System failed to authenticate");
}
} //End of event
request.send();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" value="https://yoursamplefile.txt" id="txtUrl" style="width:100%" /><br />
<input type='button' value='Download File' id='btn' />

Categories

Resources