I have a WebApi / MVC app for which I am developing an angular2 client (to replace MVC). I am having some troubles understanding how Angular saves a file.
The request is ok (works fine with MVC, and we can log the data received) but I can't figure out how to save the downloaded data (I am mostly following the same logic as in this post). I am sure it is stupidly simple, but so far I am simply not grasping it.
The code of the component function is below. I've tried different alternatives, the blob way should be the way to go as far as I understood, but there is no function createObjectURL
in URL
. I can't even find the definition of URL
in window, but apparently it exists. If I use the FileSaver.js
module I get the same error. So I guess this is something that changed recently or is not yet implemented. How can I trigger the file save in A2?
downloadfile(type: string){
let thefile = {};
this.pservice.downloadfile(this.rundata.name, type)
.subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
error => console.log("Error downloading the file."),
() => console.log('Completed file download.'));
let url = window.URL.createObjectURL(thefile);
window.open(url);
}
For the sake of completeness, the service that fetches the data is below, but the only thing it does is to issue the request and pass on the data without mapping if it succeeds:
downloadfile(runname: string, type: string){
return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
.catch(this.logAndPassOn);
}
Here's something I did in my case -
The solution is referenced from - here
For those using Redux Pattern
I added in the file-saver as @Hector Cuevas named in his answer. Using Angular2 v. 2.3.1, I didn't need to add in the @types/file-saver.
The following example is to download a journal as PDF.
The journal actions
The journal effects
The journal service
The HTTP service
The journal reducer Though this only sets the correct states used in our application I still wanted to add it in to show the complete pattern.
I hope this is helpful.
It will be better if you try to call the new method inside you
subscribe
Inside
downloadFile(data)
function we need to makeblock, link, href and file name
Downloading file through ajax is always a painful process and In my view it is best to let server and browser do this work of content type negotiation.
I think its best to have
to do it. This doesn't even require any new windows opening and stuff like that.
The MVC controller as in your sample can be like the one below:
As mentioned by Alejandro Corredor it is a simple scope error. The
subscribe
is run asynchronously and theopen
must be placed in that context, so that the data finished loading when we trigger the download.That said, there are two ways of doing it. As the docs recommend the service takes care of getting and mapping the data:
Then, on the component we just subscribe and deal with the mapped data. There are two possibilities. The first, as suggested in the original post, but needs a small correction as noted by Alejandro:
The second way would be to use FileReader. The logic is the same but we can explicitly wait for FileReader to load the data, avoiding the nesting, and solving the async problem.
Note: I am trying to download an Excel file, and even though the download is triggered (so this answers the question), the file is corrupt. See the answer to this post for avoiding the corrupt file.