I am trying to download a file on a button click, api have been hit successfully am getting 200 response, but file is not being downloaded. It's working on postman.
My code is shown below
/**** click */
downloadAsset() {
this.detailsService.getCsvReport(this.jobId).subscribe(data => {});
}
/**** Service */
getCsvReport(jobId): Observable<Object> {
const header = { Accept: "application/octet-stream" };
let endpoint: string = `${Endpoints.REPORT}jobs/${jobId}/filePath?`;
return this.get(endpoint, header).pipe(
map(report => {
return report.data.parentXml;
})
);
}
<button
class="btn btn-blue-border"
style="float: right;"
(click)="downloadAsset()"
>
Download Report
</button>
Any changes or suggestion's would be appreciated.
You can try like this
getCsvReport(jobId): Observable<Object> {
const header = { Accept: "application/octet-stream" };
let endpoint: string = `${Endpoints.REPORT}jobs/${jobId}/filePath?`;
return this.get(endpoint, header).pipe(
map(report => {
const a = document.createElement('a');
document.body.appendChild(a);
const blob: any = new Blob([report.data.parentXml], { type: 'octet/stream' });
const url = window.URL.createObjectURL(blob);
a.href = url;
a.download = data.fileName;
a.click();
window.URL.revokeObjectURL(url);
})
);
}
I think you need to set the response type of your request.
An example with http.post:
this.http.post(url + "/getFile", params, { responseType: "blob" }).subscribe(...)
I hope i can help.
Related
I'm trying to generate a xlsx file using SheetJS in node. Then in my frontend I have a button to call this route and download the file. Currently I have the following:
// backend code
export function exportExcel(req: Request, res: Response) {
try {
const workbook = utils.book_new();
const fileName = "sample";
const dataSheet = utils.json_to_sheet(sampleData);
utils.book_append_sheet(workbook, dataSheet, fileName.replace("/", ""));
const binaryWorkbook = write(workbook, {
type: "array",
bookType: "xlsx",
});
return res.status(OK).send(binaryWorkbook);
} catch (_error) {
return res.sendStatus(500)
}
}
Then in the front end I have the following:
const handleExcelExport = () => {
const { data } = await axios.get(
`/export-excel`,
{
responseType: "blob",
}
).then(response => {
const blob = new Blob([response], {
type: "application/octet-stream",
});
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
link.click();
}
}
// I simply call the above in the onClick handler in a button
<button onClick={handleExcelExport}> Export excel </button>
I can see that a download file appears when I click on the button but I can't open it. MS Excel says that ""File format or file extension is not valid..."
I fixed by changing to the following in the BE:
const binaryWorkbook = write(workbook, {
type: "array",
bookType: "xlsx",
});
and also adding headers per the docs:
res.setHeader(
"Content-Disposition",
'attachment; filename="SheetJSNode.xlsx"'
);
res.setHeader("Content-Type", "application/vnd.ms-excel");
Then in the frontend I changed the response type and content-type to
{
responseType: "arraybuffer",
}
Blob content-type
const blob = new Blob([response], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
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>
I'm trying to download pdf file from rest api using those two methods:
download(): ng.IPromise<Blob> {
return this.$http.get<ArrayBuffer>(this.urls.getUrl())
.then(response => {
return new Blob([response.data], {type: 'application/pdf'});
}
)
}
this.service.download().then( file => {
var a = document.createElement("a"),
url = URL.createObjectURL(file);
open(url)
a.href = url;
a.download = 'invoice';
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
And there appears to be some problem with the encoding of created Blob. If I call this endpoint from Postman and select "save response" then "save to a file", I get the correct pdf. When I use the presented code, file seems to be incorrectly encoded (opened with vim has generally similar structure, but different symbols).
In service you can use httpOptions like this:
download() {
const httpOptions = {
responseType: 'blob' as 'json',
};
return this.http.get<any>(
ServerUrl + `/api/getfile`,
httpOptions
);
}
also on you component you can call service and download the pdf like this:
this.api.download().subscribe(
(idata) => {
let blob = new Blob([idata], { type: 'application/pdf'
});
var downloadURL = window.URL.createObjectURL(idata);
var link = document.createElement('a');
link.href = downloadURL;
link.download = 'invoice.pdf';
link.click();
},
async (err) => {
}
);
I have a webserver that is using flask.
To download a file from the webServer, I have the following code:
#images_api_blueprint.route('/api/v1_2/download_file3', methods=['GET'])
def download_file3():
zipFullFileName1 = './foo.zip'
response1 = send_file(zipFullFileName1, as_attachment=True)
return response1
If I type "http://localhost/api/v1_2/download_file3" in the browser, the file is downloaded - good!
But I need to trigger the download programatically from my client via javascript.
When I click on a button, I reach the code below, which triggers the same url as above,
this.downloadFile3 = function (layer, zipFilename) {
let queryUrl = 'http://localhost/api/v1_2/download_file3';
fetch(queryUrl, {
method: 'get'
})
.then(function(response) {
return response;
})
.catch((err) => {
console.error('error from api/v1_2/download_file3', err);
reject(false);
});
};
But when triggering via javascript, the file is not downloaded.
What am I doing wrong?
Thanks,
Avi
Get a blob and then you can use
function downloadFile(blob, fileName)
{
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
}
or using jquery
$.ajax({
url: requestUrl,
processData: false,
dataType: 'datatype-here'
}).done(function(data) {
var blob = new Blob([data], { type: "image/png; encoding=utf8" });
saveData(blob, 'filename.png');
});
I have a problem when downloading files from my dropbox.
While using the type 'text/csv' I can download and view txt files.
Chaning the type to 'image/jpeg' or 'application/pdf' and download the filetype gives me blank file.
Am I on the right track here or is there another way this should be done?
main.service.ts
downloadFile(id) {
const headers = new Headers();
headers.append('Authorization', 'Bearer ' + this.accessToken);
const path = `{"path": "${id}"}`;
headers.append('Dropbox-API-Arg', path);
return this.http.post('https://content.dropboxapi.com/2/files/download',
null, { headers: headers });
}
main.component.ts
downloadFileBlob(data: any, name) {
const blob = new Blob([data], { type: 'image/jpeg' });
const url = window.URL.createObjectURL(blob);
window.open(url);
}
saveFile(id, name) {
this.dropbox.downloadFile(id).subscribe((data: any) => {
this.downloadFileBlob(data._body, name); });
}
Turns out I was going at this the wrong way.
Dropbox github has an example of using sharingGetSharedLinkFile, which works about the same as filesDownload.
Replace sharingGetSharedLinkFile with filesDownload and provide a file path instead of the URL.
Something like this:
function downloadFile() {
var ACCESS_TOKEN = (<HTMLInputElement> document.getElementById('access-
token')).value;
var SHARED_LINK = (<HTMLInputElement> document.getElementById('shared-
link')).value;
var dbx = new Dropbox.Dropbox({ accessToken: ACCESS_TOKEN });
dbx.filesDownload({path: SHARED_LINK})
.then(function(data) {
var downloadUrl = URL.createObjectURL((<any> data).fileBlob);
var downloadButton = document.createElement('a');
downloadButton.setAttribute('href', downloadUrl);
downloadButton.setAttribute('download', data.name);
downloadButton.setAttribute('class', 'button');
downloadButton.innerText = 'Download: ' + data.name;
document.getElementById('results').appendChild(downloadButton);
});
}