Download CSV files with HttpClient in ReactJS - javascript

I'm trying to make an API request using HttpClient to download a CSV file. I had to switch from Axios to HttpClient due to ForgeRock authentication that requires a bearer token. I'm having an issue where the response coming back does not have response.data. Where would I find the data to download to CSV?
My code:
fetchCSV = date => {
const url = "https://<URLdownloadLink>?runDate="+date;
const method = "GET";
HttpClient
.request({
url,
method,
bypassAuthentication: false,
responseType: 'blob',
init: {},
timeout: 5000
})
.then((response) => {
console.log("data", response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Asset Manager.csv');
document.body.appendChild(link);
link.click();
link.remove();
})
.catch(error => {
console.log("error", error);
})
}
The response I'm getting back is the below. The response does not have "response.data" as an object key. When I open the CSV it only has "undefined".
This is my response.body:

You are using response.data instead of response.body?
Thats why it says undefined.
Remember CSV is just plain text and response.data is undefined.
fetchCSV = date => {
const url = "https://<URLdownloadLink>?runDate=" + date;
const method = "GET";
HttpClient
.request({
url,
method,
bypassAuthentication: false,
responseType: 'blob',
init: {},
timeout: 5000
})
.then((response) => {
console.log("data", response)
const url = window.URL.createObjectURL(new Blob([response.body]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'Asset Manager.csv');
document.body.appendChild(link);
link.click();
link.remove();
})
.catch(error => {
console.log("error", error);
})
}

Related

How to download a file from an api in react

How to download a file from an API response which is in a different format ?
await axios.get(`/dashboard/reportdownload/?file_name=242424242.pdf`,{
headers: {
"Authorization": `Bearer 767958756576576576jgjhgjhg`
}
})
.then((res) => {
console.log(res?.data);
});
In response I am getting
How do download it with reactjs
You need to check if the response from the API is of type blob, then convert it. For example, if it is clear you are expecting a PDF response, try this:
await axios.get(`/dashboard/reportdownload/?file_name=242424242.pdf`, {
headers: {
'Authorization': `Bearer 767958756576576576jgjhgjhg`
}
})
.then((response) => {
let fileName = '242424242.pdf';
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
// IE variant
window.navigator.msSaveOrOpenBlob(
new Blob([response.data], {
type: 'application/pdf',
encoding: 'UTF-8'
}),
fileName
);
} else {
const url = window.URL.createObjectURL(
new Blob([response.data], {
type: 'application/pdf',
encoding: 'UTF-8'
})
);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', fileName);
document.body.appendChild(link);
link.click();
link.remove();
}
});

How to download a file using ReactJS with Axios in the frontend and FastAPI in the backend?

I am trying to create a docx file and send it to the frontend client app, so that it can be downloaded to the user's local machine. I am using FastAPI for the backend. I am using python-docx library also to create the Document.
The code below is used to create a docx file and save it to the server.
#app.post("/create_file")
async def create_file(data: Item):
document = Document()
document.add_heading("file generated", level=1)
document.add_paragraph("test")
document.save('generated_file.docx')
return {"status":"Done!"}
The below code is then used to send the created docx file as a FileResponse to the client.
#app.get("/generated_file")
async def download_generated_file():
file_path = "generated_file.docx"
return FileResponse(file_path, media_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document', filename=file_path)
On Client side (I am using ReactJS):
createFile = async () => {
const data = {
start: this.state.start,
end: this.state.end,
text: this.state.text,
};
await axios.post("http://localhost:8000/create_file", data).then(() => {
console.log("processing completed!");
});
};
downloadFile = async () => {
await axios.get("http://localhost:8000/generated_file").then((res) => {
const url = URL.createObjectURL(new Blob([res.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "generated.txt");
link.click();
});
};
The generated.docx file gets downloaded when downloadFile function is called. However, the docx file is always corrupted and doesn't open. I tried using txt file and it works fine. I need to use docx file, so what can I do?
In the Axios GET request, you have to make sure the responseType parameter is set to blob. Once you get the response from the API, you will need to pass the Blob object (i.e., response.data) to the URL.createObjectURL() function. Below is a fully working example on how to create and download a file (Document), using either Axios or Fetch API in the frontend. This answer utilises methods and code excerpts from this and this answer, as well as the answers here and here. Plesase refer to the above answers for more details on the methods used below. For demo purposes, the below example uses Jinja2Templates, but, in a similar way, you can use the scripts below in your ReactJS app.
app.py
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from fastapi.responses import FileResponse
from docx import Document
app = FastAPI()
templates = Jinja2Templates(directory="templates")
#app.get('/')
def main(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
#app.post("/create")
def create_file():
document = Document()
document.add_heading("file generated", level=1)
document.add_paragraph("test")
document.save('generated_file.docx')
return {"status":"Done!"}
#app.get("/download")
def download_generated_file():
file_path = "generated_file.docx"
return FileResponse(file_path, media_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document', filename=file_path)
Using Axios
tempaltes/index.htnl
<!DOCTYPE html>
<html>
<head>
<title>Create and Download a Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.27.2/axios.min.js"></script>
</head>
<body>
<input type="button" value="Create Document" onclick="createFile()">
<div id="response"></div><br>
<input type="button" value="Download Document " onclick="downloadFile()">
<script>
function createFile() {
axios.post('/create', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => {
document.getElementById("response").innerHTML = JSON.stringify(response.data);
})
.catch(error => {
console.error(error);
});
}
function downloadFile() {
axios.get('/download', {
responseType: 'blob'
})
.then(response => {
const disposition = response.headers['content-disposition'];
filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
if (filename.toLowerCase().startsWith("utf-8''"))
filename = decodeURIComponent(filename.replace("utf-8''", ''));
else
filename = filename.replace(/['"]/g, '');
return response.data;
})
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a); // append the element to the dom
a.click();
a.remove(); // afterwards, remove the element
})
.catch(error => {
console.error(error);
});
}
</script>
</body>
</html>
Using Fetch API
tempaltes/index.htnl
<!DOCTYPE html>
<html>
<head>
<title>Create and Download a Document</title>
</head>
<body>
<input type="button" value="Create Document" onclick="createFile()">
<div id="response"></div><br>
<input type="button" value="Download Document" onclick="downloadFile()">
<script>
function createFile() {
fetch('/create', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.text())
.then(data => {
document.getElementById("response").innerHTML = data;
})
.catch(error => {
console.error(error);
});
}
function downloadFile() {
fetch('/download')
.then(response => {
const disposition = response.headers.get('Content-Disposition');
filename = disposition.split(/;(.+)/)[1].split(/=(.+)/)[1];
if (filename.toLowerCase().startsWith("utf-8''"))
filename = decodeURIComponent(filename.replace("utf-8''", ''));
else
filename = filename.replace(/['"]/g, '');
return response.blob();
})
.then(blob => {
var url = window.URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a); // append the element to the dom
a.click();
a.remove(); // afterwards, remove the element
})
.catch(error => {
console.error(error);
});
}
</script>
</body>
</html>

Access Headers when using fetch in javascript

I am sending a zip file from my nodejs server to the browser using the following
res.set("Content-Type", "application/octet-stream");
res.set("Content-disposition", `attachment; filename="`+zip_name+`.zip"`);
res.set("Content-Length", zipBuff.length);
res.send(zipBuff);
I am then fetching it by using :
fetch("/my/url", {
method: "POST",
body: formData,
})
.then(response => {
return response.blob();
})
.then(response => {
const blob = new Blob([response], {type: 'application/zip'});
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = "blah.zip";
document.body.appendChild(a);
a.click();
});
I would like to be able to use zip_name instead of blah for the filename but I can't figure out how to access the headers (in that case Content-disposition) of the response with fetch.
Could someone please explain how it's done ?
Return blob and headers in object
fetch("/my/url", {
method: "POST",
body: formData,
})
.then(response => {
const headers = response.headers
return { blob: response.blob(), headers }
})
.then(({blob, headers}) => {
/// now you can access to **headers** and blob data
});
UPD:
To access the headers use headers.get("Header name").split('=').pop()
UPD1:
const foo = async () => {
const response = await fetch("/my/url", {
method: "POST",
body: formData,
})
if(!response.ok)
thorw new Error("Some error happend")
const blod_data = await response.blob()
const header_with_name = response.headers.get("Header name").split('=').pop()
// do something with it
}
In the first promise arrow function you have access to the HTTP response headers. If you update it to return the promise from the blob function call. Then in this nested function you can return an object that includes the header value to the outer second arrow function that processes the blob data.
Updated fetch example that includes processing the Promise returned from blob-function call.
fetch("/my/url", {
method: "POST",
body: formData,
})
.then(response => {
return response.blob().then((data) => {
return {
data: data,
filename: response.headers.get('Content-disposition'),
};
});
})
.then(({ data, filename }) => {
const blob = new Blob([data], { type: 'application/zip' });
const downloadUrl = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = downloadUrl;
a.download = filename.split('=')[1];
document.body.appendChild(a);
a.click();
});
Thank you Chapo for pointing out the issue with my previous example

"blob".then is not a function when trying to generate pdf in react js

I am sending the below response from my REST Api .
{
"value": "JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9TL0phdmFTY3JpcHQvSlModGhpcy56b29tID0gMTAwOyk+PgplbmRvYmoKNSAwIG9iago8PC9Db2xvclNwYWNlL0RldmljZUdyYXkvU3VidHlwZS9JbWFnZS9IZWlnaHQgMTUwL0ZpbHRlci9GbGF0ZURlY29kZS9UeXBlL1hPYmphkNkwEVng0L00DhlcQ6ysIV4GNaOajLDQGlbKEIuFcJchdvr0W/z0aY+lZJyy+pmGsnrODq3AQiAQCAQCiRDHKaRFx0iOEAGhh40g1P0IsjS9pXbPlEzDoYf1OKTkDjFdQ6ysIXaYzMRC17KaLtIpQxdTKUPs9Gm646LMQnT6NJeFXjazUIotCx3mrcWQabf6qG8iEAgEAoEmsW7jOIXU6BidLK2BkEZBFP88qhr9R42IKBwiqdSMSBnlGjL2GlLDZOZqMuuUIZsuQ5bp0/xSMkdl9YZdyfy1GOK2W4z6JgKBQCAQaBJLFyDTZwphpxCJjpHQGGYbPAn/JQ/DX/YVtf/Rq19v18QeBP+Sh+Gv+wra/+jVr7foAKKKKACiiigD5g/aO/wCSh6f/ANgqP/0bLXj9ewftHf8AJQ9P/wCwVH/6Nlrx+gAooooAK6DwJ/yUPw1/2FbX/wBGrXP10HgT/kofhr/sK2v/AKNWgD7fooooA8f/AGjv+Seaf/2FY/8A0VLXzBX0/wDtHf8AJPNP/wCwrH/6Klr5goAK+n/2cf8Aknmof9hWT/0VFXzBX0/+zj/yTzUP+wrJ/wCioqAPYKKKKACiiigAooooAKKKKACiiigD5g/aO/5KHp//AGCo/wD0bLXj9ewftHf8lD0//sFR/wDo2WvH6ACvt/wJ/wAk88Nf9gq1/wDRS18QV9v+BP8Aknnhr/sFWv8A6KWgDoK5/wAd/wDJPPEv/YKuv/RTV0Fc/wCO/wDknniX/sFXX/opqAPiCiiigAooooAKKKKACiiigAooooA6DwJ/yUPw1/2FbX/0atfb9fEHgT/kofhr/sK2v/o1a+36ACiiigAooooAKKKKACviDx3/AMlD8S/9hW6/9GtX2/XxB47/AOSh+Jf+wrdf+jWoA5+ug8Cf8lD8Nf8AYVtf/Rq1z9dB4E/5KH4a/wCwra/+jVoA+36KKKAPH/2jv+Seaf8A9hWP/wBFS18wV9P/ALR3/JPNP/7Csf8A6Klr5goAK+n/ANnH/knmof8AYVk/9FRV8wV9P/s4/wDJPNQ/7Csn/oqKgD2CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK4P4s+NdS8B+FbXVNLgtJp5b1LdlukZlClHbI2spzlB39a7yvH/wBo7/knmn/9hWP/ANFS0AcB/wANHeMP+gbof/fib/47R/w0d4w/6Buh/wDfib/47Xj9FAH0P4b8N2fx306TxR4okns761lOnpHpjCOMxqBICRIHO7Mrc5xgDj12P+GcfB//AEEtc/7/AMP/AMao/Zx/5J5qH/YVk/8ARUVewUAeP/8ADOPg/wD6CWuf9/4f/jVcRf8Axr8SeDtRufC+nWWlS2OjSvp9vJcRSNI0cJMalyJACxCjJAAz2FfS9fEHjv8A5KH4l/7Ct1/6NagD0D/ho7xh/wBA3Q/+/E3/AMdqnq3x98Vazo19pdxp+jLBe28lvI0cMoYK6lSRmQjOD6GvK6KACiiigD2D9nH/AJKHqH/YKk/9GxV9P18wfs4/8lD1D/sFSf8Ao2KvKACiiigAooooAKKKKACvmD9o7/8L0luZm8gMjQgMCBSL0lEIFs8YzkyMWI2MGVkYzdkYTVlZWQyMzYyNTliYWRiYTVjMGY+PDMzYWYzZTBkOGM1MjFhNzVlMjk2MGQwZDIzZjJkY2U2Pl0vUm9vdCAyMyAwIFIvU2l6ZSAyNT4+CnN0YXJ0eHJlZgoxODA2OTIKJSVFT0YK",
"transid": "transid"
}
I am trying to convert the value which is byte data to pdf using React js.
Below is my implementation:
handleSubmit = e => {
e.preventDefault();
let url = "http://localhost:8080/getPDF"
fetch(url, {
method: "POST",
body: JSON.stringify(this.state.transId),
})
responseType: 'blob'
.then(response => {
//Create a Blob from the PDF Stream
const file = new Blob(
[response.value],
{type: 'application/pdf'});
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Open the URL on new Window
window.open(fileURL);
})
.catch(error => {
console.log(error);
});
};
But I am getting the following error.Can anyone please suggest where I am going wrong?
Uncaught TypeError: "blob".then is not a function
You cannot do .then() after a string (must follow a promise). You probably meant this line responseType: 'blob' to be one line higher up, inside the object?
handleSubmit = e => {
e.preventDefault();
let url = "http://localhost:8080/getPDF"
fetch(url, {
method: "POST",
body: JSON.stringify(this.state.transId),
responseType: 'blob' // <-- MOVE THIS LINE INTO REQUEST
})
.then(response => {
//Create a Blob from the PDF Stream
const file = new Blob(
[response.value],
{type: 'application/pdf'});
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Open the URL on new Window
window.open(fileURL);
})
.catch(error => {
console.log(error);
});
};

Creating PDF from request response doesn't work with axios but works in native xhr

In order to force download PDF from server I tried to use axios and native xhr object. The reason is that I have to send post request, because I pass too much data to server, so the option with simple link (like site.ru/download-pdf won't work for me).
Even though I finally managed to do this with Xhr, I still don't have a clue why axios way doesn't work.
Here is how I do this with xhr and it 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.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()
}
};
xhr.send("data=" + data);
Here is "axios-way" and I actually get PDF with correct number of pages, but they are all empty:
axios.post(`order-results/${id}/export-pdf`, {
data,
responseType: 'arraybuffer'
}).then((response) => {
let blob = new Blob([response.data], { type:"application/pdf" })
let link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = 'Results.pdf'
link.click()
})
Axios is already configured to send Authorization token.
I put Application/x-www-form-urlencoded in xhr because otherwise I couldn't get data in server side.
Even though xhr works, I'd prefer to use axios since I use it everywhere and I'm just curios what I'm doing wrong. I tried different solutions, and only native xhr did the job.
The following works for me:
axios.post("http://localhost:8080/reports/my-report/",
data,
{
responseType: 'arraybuffer',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/pdf'
}
})
.then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf'); //or any other extension
document.body.appendChild(link);
link.click();
})
.catch((error) => console.log(error));
Let me know if this helps.
Cheers!
For whatever reason the pdf is downloading but any content passed is not appearing resulting in a blank pdf.
I found this code snippet that is similar but results in a pdf with content.
axios({
url: '/pdf',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf');
document.body.appendChild(link);
link.click();
});
there was some feedback stating that it didnt work on IE 11, I have not tested it but a solution was posted using FileSaver.js
axios({
url: '/pdf',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
FileSaver.saveAs(new Blob([response.data]));
});
There is a special responseType: 'blob':
axios.post(`order-results/${id}/export-pdf`, {
data,
responseType: 'blob'
}).then((response) => {
let link = window.URL.createObjectURL(blob)
link.download = 'Results.pdf'
link.click()
})
Or you can use window.open method:
axios.post(`order-results/${id}/export-pdf`, {
data,
responseType: 'blob'
}).then((response) => {
window.open(URL.createObjectURL(response.data));
})
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();
});

Categories

Resources