Option to user to download excel file in Angular - excel

How to download a excel file from server on button click in Angular 8. I am new to Angular and I read through different posts which said to file-saver library. Do we need to install a library to work out download option or not. Can anyone please share the code and insights.

You can use this for example:
const resp = await this.httpClient.get(`{yourendpoint}`, { responseType: 'blob' }).toPromise();
const url = window.URL.createObjectURL(resp);
window.open(url);

If your getting blob from you backend then you can do something like this:
let a = document.createElement('a');
a.href = URL.createObjectURL(data);
a.download = 'FILE_NAME';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
Where 'data' is response from your api.
PS: Don't forget to add header responseType: 'blob'

Related

Axios Excel file download using POST has missing rows

I'm trying to download an Excel file using an axios POST request. The backend seems to be working fine as I can download the Excel file using Swagger UI and Postman. The problem happens when I try to download using axios and I only get an Excel file with headers but missing all the rows of data. Here is my axios request:
await axios.post(API.viewReport.reportsNotLoginExcel, { data: data },
{
responseType: 'arraybuffer', // Important
}
).then(async (response) => {
let blob = new Blob([response.data], {type:'application/vnd.ms-excel'});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", "file.xlsx");
document.body.appendChild(link);
link.click();
link.remove();
});
I have tried both responseType: 'arraybuffer' and responseType: 'blob'
And here is my Spring controller:
#PostMapping(path = "/reports/not-login/excel", produces = "application/vnd.ms-excel")
public ResponseEntity<Resource> doDownloadNotYetLogin(#RequestHeader(value = "iv-user") String ivUser,
#Valid #RequestBody DownloadRequest downloadRequest) {
InputStreamResource file = new InputStreamResource(viewReportService.doDownloadNotYetLogin(downloadRequest));
String filename = "NotYetLoginReport.xlsx";
return ResponseEntity.ok().header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
.contentType(MediaType.parseMediaType("application/vnd.ms-excel")).body(file);
}
In both Swagger UI and Postman, I am able to download the file with the correct data, so I assume there is nothing wrong with my backend.
Excel file with correct data
But downloading the file using axios gives me a file with only the headers and no rows of data.
Excel file with missing rows of data
There are no error messages or warnings from either frontend or backend.
I've tried external libraries like js-file-download and solutions from other posts but so far with no luck. I'm confused because I am able to get the file but somehow part of it is missing, and I haven't found any post facing this problem either.
Do let me know if any other information would be useful to show.

React+Node Express, downloaded docx file is corrupted

I've read through alot of similiar questions, tried alot of suggested solutions, and none worked for me. So, i send the file from the backend using "res.download('directory/' + filename)", and judging from the response headers, i do get the correct file. There are no other files in the folder i'm sending from, and the original file is 14KB. However the 'data' part of the response is around 21KB. This is what i do with the response on the web app to get the file:
await axios.get(`api` + file.id,
{headers: {'x-access-token': token}}, { responseType: 'arraybuffer' }
).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data])); //specifying the type here doesn't help
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${filename}`); //filename = the name of the file i'm trying to download
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
})
And the file i get in result is also around 21KB, and does not open in word due to it being corrupt.
Fiddled a bit with the axios.get syntax, put the "responseType" togather with the config which has headers. And now the file i get is not corrupted o_o
axios.get(`api/` + file.id,
{
headers:
{
'x-access-token': token
},
responseType: 'arraybuffer'
}
)
At first i thought that the file size was different, but it's not. So that definately fixed it lol. That's what i get from being a noob in js.
You may need to add the file extension at the end of the filename like this:
link.setAttribute('download', `${filename}.docx`);

upload generated pdf without temporarily writing any file

I want to upload pdf file created by pdfmake to remote server. For that I am using following code roughly.
const doc = printer.createPdfKitDocument(docDefinition);
doc.pipe(fs.createWriteStream(filename))
var form = new FormData();
form.append("file", fs.createReadStream(filename));
let response=await axios.post(url, form, {headers: {
...form.getHeaders(),
}});
But issue with above approach is it requires to create file locally. I want to avoid that and want to take output from pdfmake and send it directly to server. How can I do that ?

Nodejs - React download file from s3 bucket using pre-signed url

I am trying to make an onClick button to download a file from S3 bucket using pre-signet url. The problem comes when I received my url. I want an automatic redirect or kind of. In other words, how can I lunch the download file after getting back my signed url?
this is my document list
The onClick event is on the Download button.
redux action
Redux action call my nodejs route
api route nodejs
Ask for pre-signed url then send it to my redux reducer.
Now in my front-end page, I got my link but I want an automatic redirect to start the file download.
Part of Component
Hope my first post isn't too messy.
I resolved my problem with a redux action. With one click I call my action, who return my pre-signed URL, then automatically click the link. This trigger download event with the original file name when I upload it to S3.
export const downDoc = (docId) => async dispatch => {
const res = await axios({ url: 'myApiCall', method: 'GET', responseType: 'blob' })
.then((response) => {
console.log(response)
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${docId.originalName}`);
document.body.appendChild(link);
link.click();
});
The other answer does direct DOM manipulation, creates a blob, which looks as though it buffers the whole file in memory before sending it to the user and also creates a new link each time you download. A react-y of doing is:
const downloadFileRef = useRef<HTMLAnchorElement | null>(null);
const [downloadFileUrl, setDownloadFileUrl] = useState<string>();
const [downloadFileName, setDownloadFileName] = useState<string>();
const onLinkClick = (filename: string) => {
axios.get("/presigned-url")
.then((response: { url: string }) => {
setDownloadFileUrl(response.url);
setDownloadFileName(filename);
downloadFileRef.current?.click();
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<a onClick={() => onLinkClick("document.pdf")} aria-label="Download link">
Download
</a>
<a
href={downloadFileUrl}
download={downloadFileName}
className="hidden"
ref={downloadFileRef}
/>
</>)
See here for more info https://levelup.gitconnected.com/react-custom-hook-typescript-to-download-a-file-through-api-b766046db18a
The way I did it was different and has the advantage of being able to see the progress of the download as the file is being downloaded. If you're downloading a large file then it makes a difference UX wise as you see feedback immediately.
What I did was:
When creating the S3 presigned URL I set the content-disposition to `attachment
I used an anchor element to download the actual item <a url='https://presigned-url' download>Download me</a>
Others have mentioned simulating a click within the DOM or React, but another option is to use window.open(). You can set the target attribute to _blank to open a tab, but you do need window.open() inside the click event to prevent popup blockers from stopping the functionality. There's some good discussion on the subject here. I found this to be a better solution than simulating a click event.
Here's an example (though there may be more needed depending on how you fetch the signed_url).
function downloadDocument() {
const signedurlPromise = fetch("/signed_url")
signedurlPromise.then((response) => {
window.open(response.signed_url, "_blank");
})
}

Downloading files from expressJS (sendFile) server to VueJS, is returning corrupted files?

I have an API written in expressjs, that sends a file when provided with a fileID.
The backend is working fine, as it sends the correct file (uncorrupted) when the route url is typed directly int the browser.
ex. http://localhost:8080/download?id=1234 (downloads all files just fine ie.txt, xlsx, jpg, zip)
My express route actually uses res.download, which is really jsut a wrapper for sendFile.
When I try calling these route urls from a Vue http get request, it only returns txt files uncorrupted. All other files download, but they can be opened, due to corruption.
Can anyone please point me in the right direction as to why its not working in Vue?
For clarity, "item" is passed as an argument to this function.
this.$http.get("http://localhost:8000/files/download", {params:{id:item._id}}, {responseType: 'arraybuffer'})
.then(response => {
var blob = new Blob([response.data], {type:response.headers.get('content-type')});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = item.filename;
link.click();
})
For reference, this is my express route
Shout out to #Helpinghand for helping me troubleshoot this.
"I found the solution within the link you posted. The problem is that i was explicitly sending params in its own object before assigning "content-type". The example you posted concatenates the query params to the url. After making this switch, its working perfectly".
this.$http.get(`http://localhost:8000/files/download?id=${item._id}`, {responseType: 'arraybuffer'})
.then(response => {
console.log(response.headers.get('content-type'));
console.log(response);
var blob = new Blob([response.body], {type:response.headers.get('content-type')});
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = item.filename_version;
link.click();
})

Resources