Recieving a Zip file as response on AJAX request - javascript

So I'm working on a website that needs to make a call to the server and it returns a zip file, the thing is that I'm not entierly sure I'm doing everything right. The code looks kind of like this:
function download(){
if($('.download').hasClass('activeBtn')){
$.ajax({
type: 'GET',
url: someUrl,
contentType: 'application/zip',
dataType: 'text',
headers: {
'Api-Version': '3.4'
}
}).then(function (data) {
console.log(data); //Basically prints the byte array
//Here I should build the file and download it
});
}
}
As you can see I need to makeup the file with the byte array that is in the response, how can I do that?

An approach utilizing XMLHttpRequest(); check if a element has download property, if true, set download property to an objectURL; else, use window.open() with parameter objectURL of Blob response
function downloadFile(url, headers, filename) {
function handleFile(data) {
console.log(this.response || data);
var file = URL.createObjectURL(this.response || data);
filename = filename || url.split("/").pop();
var a = document.createElement("a");
// if `a` element has `download` property
if ("download" in a) {
a.href = file;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} else {
// use `window.open()` if `download` not defined at `a` element
window.open(file)
}
}
var request = new XMLHttpRequest();
request.responseType = "blob";
request.onload = handleFile;
request.open("GET", url);
for (var prop in headers) {
request.setRequestHeader(prop, headers[prop]);
}
request.send();
}
downloadFile("/path/to/resource/", {"x-content": "abc"}, "filename.zip")
jQuery version using fork of jquery-ajax-blob-arraybuffer.js
/**
*
* jquery.binarytransport.js
*
* #description. jQuery ajax transport for making binary data type requests.
* #version 1.0
* #author Henry Algus <henryalgus#gmail.com>
*
*/
// use this transport for "binary" data type
$.ajaxTransport("+binary", function(options, originalOptions, jqXHR){
// check for conditions and support for blob / arraybuffer response type
if (window.FormData && ((options.dataType && (options.dataType == 'binary'))
|| (options.data
&& ((window.ArrayBuffer && options.data instanceof ArrayBuffer)
|| (window.Blob && options.data instanceof Blob))))
)
{
return {
// create new XMLHttpRequest
send: function(headers, callback){
// setup all variables
var xhr = new XMLHttpRequest(),
url = options.url,
type = options.type,
async = options.async || true,
// blob or arraybuffer. Default is blob
dataType = options.responseType || "blob",
data = options.data || null,
username = options.username || null,
password = options.password || null;
xhr.addEventListener('load', function(){
var data = {};
data[options.dataType] = xhr.response;
// make callback and send data
callback(xhr.status
, xhr.statusText
, data
, xhr.getAllResponseHeaders());
});
xhr.open(type, url, async, username, password);
// setup custom headers
for (var i in headers ) {
xhr.setRequestHeader(i, headers[i] );
}
xhr.responseType = dataType;
xhr.send(data);
},
abort: function(){
jqXHR.abort();
}
};
}
});
function downloadFile(url, headers, filename) {
return $.ajax({
url:url,
dataType:"binary",
processData: false,
headers:headers
})
.then(function handleFile(data) {
console.log(this.response || data);
var file = URL.createObjectURL(this.response || data);
filename = filename || url.split("/").pop();
var a = document.createElement("a");
// if `a` element has `download` property
if ("download" in a) {
a.href = file;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} else {
// use `window.open()` if `download` not defined at `a` element
window.open(file)
}
})
}
downloadFile("/path/to/resource/", {"x-custom-header":"abc"}, "filename.zip");
Just have to download it, that's all
You can use <a> element, download attribute
$("<a>", {href: someUrl,
download: "filename.zip"
}).appendTo("body")[0].click()
Alternatively parse file using a library, e.g., zip.js, create multiple or single downloadable .zip from data contained within file.
Create an objectURL of each file, download each file using a element.
If download attribute is not available at browser, you can use data URI of file object with MIME type set to application/octet-stream to download file

I ended up using fetch api. For me its more clear whats goin on:
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(dwnloadModel)
})
.then((response) => {
response.blob().then((blob) => {
const downloadUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.setAttribute('href', downloadUrl);
link.setAttribute('download', 'file');
link.style.display = 'none';
document.body.appendChild(link);
link.click();
window.URL.revokeObjectURL(link.href);
document.body.removeChild(link);
})
});
I basicly take the blob from my response and put it to url, which i then append to dom as a 'a' tag and click it by js. Once is clicked i clean up the a tag and revoke the url. works fine for me.

Related

How to set multiple headers data with XMLHttpRequest in async mode?

My api call requires me to pass the api key in the headers, but I'm getting error back from the api service {"error":"2424452","message":"Invalid Api Key"}
I know my api key is valid as I can make the same api call in Python just fine, example:
req = requests.Session()
req.headers.update({'x-api-key': 'my-api-key', 'X-Product': 'my-product-name'})
req.get(url)
But in javscript, the same call errors out. I believe I'm not setting the headers correctly or something?
var req = new XMLHttpRequest();
req.onreadystatechange=handleStateChange;
req.open("GET", "url", true);
req.setRequestHeader("Host", "api.domain.com", "x-api-key", "my-api-key", "X-Product", "my-product-name");
req.send();
This XMLHttpRequest is not a browser call, rather in an application that support XMLHttpRequest.
setRequestHeader sets one header and takes two arguments (the name and the value).
If you want to set multiple headers, then call setRequestHeader multiple times. Don't add extra arguments to the first call.
In case you don't want to set multiple headers explicitly you can use
function setHeaders(headers){
for(let key in headers){
xhr.setRequestHeader(key, headers[key])
}
}
setHeaders({"Host":"api.domain.com","X-Requested-With":"XMLHttpRequest","contentType":"application/json"})
downloadReportFile(id, query): Observable<Object[]> {
var url = `${environment.baseUrl}report?report_name=${id}${query}`;
return Observable.create(observer => {
let xhr = new XMLHttpRequest();
xhr.open('GET', `${url}`, true);
xhr.setRequestHeader(environment.AUTH_TOKEN_HEADER_KEY, 'Bearer '+
localStorage.getItem(environment.AUTH_TOKEN_STORE_KEY));
xhr.responseType = 'blob';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
let filename = "Claim_Report.csv"
var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
var blob = new Blob([xhr.response], { type: "text/plain;charset=utf-8" });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename);
return;
}
const blobURL = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', filename);
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
setTimeout(() => {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(blobURL);
}, 100);
} else {
observer.error(xhr.response);
}
}
}
xhr.send();
});
}

Force download GET request using axios

I'm using vuejs 2 + axios.
I need to send a get request, pass some params to server, and get a PDF as a response. Server uses Laravel.
So
axios.get(`order-results/${id}/export-pdf`, { params: { ... }})
makes successful request but it does not start force downloading, even though server returns correct headers.
I think this is a typical situation when you need to, say, form a PDF report and pass some filters to server. So how could it be accomplished?
Update
So actually I found a solution. However the same approach didn't work with axios, don't know why, that's why I used raw XHR object. So the solution is to create a blob object and user createUrlObject function. Sample example:
let xhr = new XMLHttpRequest()
xhr.open('POST', Vue.config.baseUrl + `order-results/${id}/export-pdf`, true)
xhr.setRequestHeader("Authorization", 'Bearer ' + this.token())
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
xhr.responseType = 'arraybuffer'
xhr.onload = function(e) {
if (this.status === 200) {
let blob = new Blob([this.response], { type:"application/pdf" })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'Results.pdf'
link.click()
}
}
Important: you should have array buffer as response type
However, the same code written in axios returns PDF which is empty:
axios.post(`order-results/${id}/export-pdf`, {
data,
responseType: 'arraybuffer'
}).then((response) => {
console.log(response)
let blob = new Blob([response.data], { type: 'application/pdf' } ),
url = window.URL.createObjectURL(blob)
window.open(url); // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
})
You're getting empty PDF 'cause no data is passed to the server. You can try passing data using data object like this
axios
.post(`order-results/${id}/export-pdf`, {
data: {
firstName: 'Fred'
},
responseType: 'arraybuffer'
})
.then(response => {
console.log(response)
let blob = new Blob([response.data], { type: 'application/pdf' }),
url = window.URL.createObjectURL(blob)
window.open(url) // Mostly the same, I was just experimenting with different approaches, tried link.click, iframe and other solutions
})
By the way I gotta thank you so much for showing me the hint in order to download pdf from response. Thank ya :)
var dates = {
fromDate: 20/5/2017,
toDate: 25/5/2017
}
The way in which I have used is,
axios({
method: 'post',
url: '/reports/interval-dates',
responseType: 'arraybuffer',
data: dates
}).then(function(response) {
let blob = new Blob([response.data], { type: 'application/pdf' })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'Report.pdf'
link.click()
})
Try this:
It works perfectly for me with compatibility for Internet Explorer 11 (createObjectURL doesn't work on Explorer 11)
axios({
url: 'http://vvv.dev',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
if (!window.navigator.msSaveOrOpenBlob){
// BLOB NAVIGATOR
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'download.pdf');
document.body.appendChild(link);
link.click();
}else{
// BLOB FOR EXPLORER 11
const url = window.navigator.msSaveOrOpenBlob(new Blob([response.data]),"download.pdf");
}
});
https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
I tried some of the above suggested approaches but in my case the browser was sending me the popup block warning.
The code described below worked for me:
axios.get(url, {responseType: 'arraybuffer'})
.then(function (response) {
var headers = response.headers();
var blob = new Blob([response.data],{type:headers['content-type']});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Your_file_name";
link.click();
});
I don't think its possible to do this in axios or even AJAX. The file will be kept in memory, i.e. you cannot save file to disk. This is because JavaScript cannot interact with disk. That would be a serious security issue and it is blocked in all major browsers.
You can construct your URL in front-end and download it in the following way:
var url = 'http://example.com/order-results/' + id + '/export-pdf?' + '..params..'
window.open(url, '_blank');
Hope this helps!
this code works for me :
let xhr = new XMLHttpRequest()
xhr.open('POST', Vue.config.baseUrl + `order-results/${id}/export-pdf`, true)
xhr.setRequestHeader("Authorization", 'Bearer ' + this.token())
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
xhr.responseType = 'arraybuffer'
xhr.send()
xhr.onload = function(e) {
if (this.status === 200) {
let blob = new Blob([this.response], { type:"application/pdf" })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'Results.pdf'
link.click()
}
}
I had similar issues- I ended up creating link and downloading from there.
I put more details of how in the answer on another stackoverflow question.

Kendo server export POST request headers

I am using Kendo's server side export functionality to be able to export xlsx/pdfs from the server. This is my configuration
kendo.saveAs({
dataURI: workbook.toDataURL(),
fileName: "STRResult.xlsx",
proxyURL:'/my/kendo/export/kendoServerExport',
forceProxy:true,
proxyTarget: "_blank"
});
The problem here is that I have a server (without code access) in Spring MCV and it has a global filter that checks for certain headers. I want to be able to set the required headers on this post request.
What configuration do I need to use to get it working?
TIA
I've provided a solution for this on the Kendo UI forum.
You can find it HERE
Solution as described on the forum:
I've build a solution for this based on some info found elsewhere on the internet.
I would like to share it with you, so perhaps Telerik might want to use is in some future version ;)
//#region -- Download file and save data to a local file --
// Invoke call to webservice and save file
// url - Url to get the file from
// method - GET/POST method
// defaultFilename - Default filename if none was given from the server
, saveAs: function (url, method, defaultFilename)
{
var caller = this;
Insad.ajaxMaskUI({
url: url,
type: method,
headers: {
'Authorization': Insad.GetBaseAuthenticationToken()
// ... Put your headers in here ...
},
maskUI: Insad.defaultMaskUI
, maskPageMsg: 'Downloading file'
})
.fail(function (xhr, textStatus, errorThrown) {
//console.log('error:', textStatus, errorThrown, xhr.getAllResponseHeaders(), xhr);
var msg = xhr.responseText;
if (msg === undefined) {
msg = 'Unknown error';
}
console.log('FileDownload failed -> Error handler. Errormessage: ', msg);
ShowWarning(msg);
caller.handleUIErrorDownload();
})
.done(function (data, textStatus, xhr) {
caller.handleUISuccessDownload(xhr, defaultFilename);
});
}
, handleUIErrorDownload: function ()
{
console.log('handleUIError');
// TODO: Reset stuff / give user more info?
// ... Put your code in here ...
}
, handleUISuccessDownload: function (xhr, defaultFilename)
{
console.log('handleUISuccess');
// Save file
var filename = Insad.GetFilenameFromContent(xhr);
if (filename === '') {
filename = defaultFilename;
}
Insad.SaveDataToFile(filename, xhr.responseText, xhr.getResponseHeader('Content-Type'));
// TODO: Give user more info?
// ... Put your code in here ...
}
//#endregion
//#region -- Save data to a local file --
// Get the filename from the Content-Disposition in the response xhr
, GetFilenameFromContent: function (xhr)
{
var filename = '';
var disposition = xhr.getResponseHeader('Content-Disposition');
// Only when inline or attachment are supported at this moment
if (disposition && (disposition.indexOf('attachment') !== -1 || disposition.indexOf('inline') !== -1)) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
return filename;
}
// The function that will write the data to a file :)
, SaveDataToFile: function (filename, data, mimeType)
{
var dataFileAsBlob = new Blob([data], {type:mimeType});
var downloadLink = document.createElement("a");
downloadLink.download = filename;
downloadLink.innerHTML = "Download";
if (window.webkitURL !== null) { // Is it Chrome based?
// Chrome allows the link to be clicked
// without actually adding it to the DOM.
downloadLink.href = window.webkitURL.createObjectURL(dataFileAsBlob);
}
else {
// Firefox requires the link to be added to the DOM
// before it can be clicked.
downloadLink.href = window.URL.createObjectURL(dataFileAsBlob);
downloadLink.onclick = destroyClickedElement; // --> TODO: ?? TEST ?? On Opera use: downloadLink.onclick = document.body.removeChild(event.target);
downloadLink.style.display = "none";
document.body.appendChild(downloadLink);
}
// Force download
downloadLink.click();
}
//#endregion
This is a part from our base library (Insad).
I think you get the idea. If you're missing something I would be happy to provide it to you :)
Instead of calling the 'old' saveAs like:
kendo.saveAs({
dataURI: url,
fileName: defaultFilename,
forceProxy: false
});
You can now use:
Insad.saveAs({
url: url,
method: 'GET', // or use 'POST' if you really must ;)
defaultFilename: defaultFilename
});
I left out the animation part but that shouldn't be to hard I think :)

Download pdf file using jquery ajax

I want to download a pdf file for jquery ajax response. Ajax response contains pdf file data. I tried this solution. My code is given below but I always get a blank pdf.
$(document).on('click', '.download-ss-btn', function () {
$.ajax({
type: "POST",
url: 'http://127.0.0.1:8080/utils/json/pdfGen',
data: {
data: JSON.stringify(jsonData)
}
}).done(function (data) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Sample.pdf";
link.click();
});
});
jQuery has some issues loading binary data using AJAX requests, as it does not yet implement some HTML5 XHR v2 capabilities, see this enhancement request and this discussion
Given that, you have one of two solutions:
First solution, abandon JQuery and use XMLHTTPRequest
Go with the native HTMLHTTPRequest, here is the code to do what you need
var req = new XMLHttpRequest();
req.open("GET", "/file.pdf", true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
console.log(blob.size);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Dossier_" + new Date() + ".pdf";
link.click();
};
req.send();
Second solution, use the jquery-ajax-native plugin
The plugin can be found here and can be used to the XHR V2 capabilities missing in JQuery, here is a sample code how to use it
$.ajax({
dataType: 'native',
url: "/file.pdf",
xhrFields: {
responseType: 'blob'
},
success: function(blob){
console.log(blob.size);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="Dossier_" + new Date() + ".pdf";
link.click();
}
});
I am newbie and most of the code is from google search. I got my pdf download working with the code below (trial and error play). Thank you for code tips (xhrFields) above.
$.ajax({
cache: false,
type: 'POST',
url: 'yourURL',
contentType: false,
processData: false,
data: yourdata,
//xhrFields is what did the trick to read the blob to pdf
xhrFields: {
responseType: 'blob'
},
success: function (response, status, xhr) {
var filename = "";
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition) {
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches !== null && matches[1]) filename = matches[1].replace(/['"]/g, '');
}
var linkelem = document.createElement('a');
try {
var blob = new Blob([response], { type: 'application/octet-stream' });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
window.navigator.msSaveBlob(blob, filename);
} else {
var URL = window.URL || window.webkitURL;
var downloadUrl = URL.createObjectURL(blob);
if (filename) {
// use HTML5 a[download] attribute to specify filename
var a = document.createElement("a");
// safari doesn't support this yet
if (typeof a.download === 'undefined') {
window.location = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.target = "_blank";
a.click();
}
} else {
window.location = downloadUrl;
}
}
} catch (ex) {
console.log(ex);
}
}
});
You can do this with html5 very easily:
var link = document.createElement('a');
link.href = "/WWW/test.pdf";
link.download = "file_" + new Date() + ".pdf";
link.click();
link.remove();
For those looking a more modern approach, you can use the fetch API. The following example shows how to download a PDF 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/pdf'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "file.pdf";
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][1].
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.
Try this:
$(document).on('click', '.download-ss-btn', function () {
$.ajax({
type: "POST",
url: 'http://127.0.0.1:8080/utils/json/pdfGen',
data: {
data: JSON.stringify(jsonData)
},
headers: {
'Content-Type': 'application/json; charset=utf-8'
}
}).done(function (data) {
let binaryString = window.atob(data);
let binaryLen = binaryString.length;
let bytes = new Uint8Array(binaryLen);
for (let i = 0; i < binaryLen; i++) {
let ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
var blob = new Blob([data], {type: "application/pdf"});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = "Sample.pdf";
link.click();
});
});

How can I download a file using window.fetch?

If I want to download a file, what should I do in the then block below?
function downloadFile(token, fileId) {
let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
return fetch(url, {
method: 'GET',
headers: {
'Authorization': token
}
}).then(...);
}
Note: The code is on the client-side.
This is more shorter and efficient, no libraries only fetch API
const url ='http://sample.example.file.doc'
const authHeader ="Bearer 6Q************"
const options = {
headers: {
Authorization: authHeader
}
};
fetch(url, options)
.then( res => res.blob() )
.then( blob => {
var file = window.URL.createObjectURL(blob);
window.location.assign(file);
});
This solution does not allow you to change filename for the downloaded file. The filename will be a random uuid.
EDIT: syg answer is better. Just use downloadjs library.
The answer I provided works well on Chrome, but on Firefox and IE you need some different variant of this code. It's better to use library for that.
I had similar problem (need to pass authorization header to download a file so this solution didn't helped).
But based on this answer you can use createObjectURL to make browser save a file downloaded by Fetch API.
getAuthToken()
.then(token => {
fetch("http://example.com/ExportExcel", {
method: 'GET',
headers: new Headers({
"Authorization": "Bearer " + token
})
})
.then(response => response.blob())
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = "filename.xlsx";
document.body.appendChild(a); // we need to append the element to the dom -> otherwise it will not work in firefox
a.click();
a.remove(); //afterwards we remove the element again
});
});
I temporarily solve this problem by using download.js and blob.
let download = require('./download.min');
...
function downloadFile(token, fileId) {
let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
return fetch(url, {
method: 'GET',
headers: {
'Authorization': token
}
}).then(function(resp) {
return resp.blob();
}).then(function(blob) {
download(blob);
});
}
It's working for small files, but maybe not working for large files. I think I should dig Stream more.
function download(dataurl, filename) {
var a = document.createElement("a");
a.href = dataurl;
a.setAttribute("download", filename);
a.click();
return false;
}
download("data:text/html,HelloWorld!", "helloWorld.txt");
or:
function download(url, filename) {
fetch(url).then(function(t) {
return t.blob().then((b)=>{
var a = document.createElement("a");
a.href = URL.createObjectURL(b);
a.setAttribute("download", filename);
a.click();
}
);
});
}
download("https://get.geojs.io/v1/ip/geo.json","geoip.json")
download("data:text/html,HelloWorld!", "helloWorld.txt");
Using dowloadjs. This will parse the filename from the header.
fetch("yourURL", {
method: "POST",
body: JSON.stringify(search),
headers: {
"Content-Type": "application/json; charset=utf-8"
}
})
.then(response => {
if (response.status === 200) {
filename = response.headers.get("content-disposition");
filename = filename.match(/(?<=")(?:\\.|[^"\\])*(?=")/)[0];
return response.blob();
} else {
return;
}
})
.then(body => {
download(body, filename, "application/octet-stream");
});
};
Here is an example using node-fetch for anyone that finds this.
reportRunner({url, params = {}}) {
let urlWithParams = `${url}?`
Object.keys(params).forEach((key) => urlWithParams += `&${key}=${params[key]}`)
return fetch(urlWithParams)
.then(async res => ({
filename: res.headers.get('content-disposition').split('filename=')[1],
blob: await res.blob()
}))
.catch(this.handleError)
}
As per some of the other answers, you can definitely use window.fetch and download.js to download a file. However, using window.fetch with blob has the restriction on memory imposed by the browser, and the download.js also has its compatibility restrictions.
If you need to download a big-sized file, you don't want to put it in the memory of the client side to stress the browser, right? Instead, you probably prefer to download it via a stream. In such a case, using an HTML link to download a file is one of the best/simplest ways, especially for downloading big-sized files via a stream.
Step One: create and style a link element
You can make the link invisible but still actionable.
HTML:
<a href="#" class="download-link" download>Download</a>
CSS:
.download-link {
position: absolute;
top: -9999px;
left: -9999px;
opacity: 0;
}
Step Two: Set the href of the link, and trigger the click event
JavaScript
let url = `https://www.googleapis.com/drive/v2/files/${fileId}?alt=media`;
const downloadLink = document.querySelector('.download-link')
downloadLink.href = url + '&ts=' + new Date().getTime() // Prevent cache
downloadLink.click()
Notes:
You can dynamically generate the link element if necessary.
This approach is especially useful for downloading, via a stream, big-sized files that are dynamically generated on the server side
I tried window.fetch but that ended up being complicated with my REACT app
now i just change window.location.href and add query params like the jsonwebtoken and other stuff.
///==== client side code =====
var url = new URL(`http://${process.env.REACT_APP_URL}/api/mix-sheets/list`);
url.searchParams.append("interval",data.interval);
url.searchParams.append("jwt",token)
window.location.href=url;
// ===== server side code =====
// on the server i set the content disposition to a file
var list = encodeToCsv(dataToEncode);
res.set({"Content-Disposition":`attachment; filename=\"FileName.csv\"`});
res.status(200).send(list)
the end results actually end up being pretty nice, the window makes request and downloads the file and doesn't event switch move the page away, its as if the window.location.href call was like a lowkey fetch() call.
A similar but cleaner and more reliable solution IMO.
On your fetch function...
fetch(...)
.then(res =>
{
//you may want to add some validation here
downloadFile(res);
}
)
and the downloadFile function is...
async function downloadFile(fetchResult) {
var filename = fetchResult.headers.get('content-disposition').split('filename=')[1];
var data = await fetchResult.blob();
// It is necessary to create a new blob object with mime-type explicitly set
// otherwise only Chrome works like it should
const blob = new Blob([data], { type: data.type || 'application/octet-stream' });
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// IE doesn't allow using a blob object directly as link href.
// Workaround for "HTML7007: One or more blob URLs were
// revoked by closing the blob for which they were created.
// These URLs will no longer resolve as the data backing
// the URL has been freed."
window.navigator.msSaveBlob(blob, filename);
return;
}
// Other browsers
// Create a link pointing to the ObjectURL containing the blob
const blobURL = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = blobURL;
tempLink.setAttribute('download', filename);
// Safari thinks _blank anchor are pop ups. We only want to set _blank
// target if the browser does not support the HTML5 download attribute.
// This allows you to download files in desktop safari if pop up blocking
// is enabled.
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
setTimeout(() => {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(blobURL);
}, 100);
}
(downloadFile function source: https://gist.github.com/davalapar/d0a5ba7cce4bc599f54800da22926da2)
No libraries only fetch API. Also you can change the file name
function myFetch(textParam, typeParam) {
fetch("http://localhost:8000/api", {
method: "POST",
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
},
body: JSON.stringify({
text: textParam,
barcode_type_selection: typeParam,
}),
})
.then((response) => {
return response.blob();
})
.then((blob) => {
downloadFile(blob);
});
}
Here is the download file function
function downloadFile(blob, name = "file.pdf") {
const href = URL.createObjectURL(blob);
const a = Object.assign(document.createElement("a"), {
href,
style: "display:none",
download: name,
});
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(href);
a.remove();
}

Categories

Resources