angular: Angular2 download excel file from Web API, file is corrupt
**I’m submitting a bug report
I believe that I have encountered a bug in angular2 with downloading a file from a Web API.
I know you want a plunkr that demonstrates the problem, but I’m not sure how to do that since we need a web api, as well as the angular2 code.
Current behavior
I am trying to download a file that I created with ClosedXML. I have verified that the file is not corrupt but, for some reason, it works just with Angular1, not Angular2.
Minimal reproduction of the problem with instructions
The web api code to return the file is:
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.OK;
response.Content = new ByteArrayContent(ms.GetBuffer());
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
return response;
In Angular2, in my web service:
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('responseType', 'arrayBuffer');
this.observableDataGet = this._http.post(`${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, {headers: this.getHeaders()})
.map(response => {
if (response.status == 400) {
return "FAILURE";
} else if (response.status == 200) {
var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
var blob = new Blob([response.arrayBuffer()], { type: contentType });
return blob;
}
})
and in my component:
.subscribe(blob => {
var downloadUrl= URL.createObjectURL(blob);
window.open(downloadUrl);
},
A file IS downloaded, but it is corrupt when I try to access it and is TWICE the size of the file when downloaded with Angular1.
Expected behavior If I call the SAME API with Angular1, the file is downloaded fine. My service code:
function generateMonthlySpreadsheet(header) {
var request = $http({
method: "post",
responseType: 'arraybuffer',
url: TEST_API_URL + 'Report/MonthlySpreadsheet',
timeout: 30000,
headers: header
});
return ( request.then(handleSuccess, handleError) );
}
where handleSuccess returns response.data (which I can’t get at for angular2)
and the code to invoke the service:
alertAppService.generateMonthlySpreadsheet(header).then(function (data){
var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
Interestingly enough, in Angular2, if I simply change my webservice to a GET (I wanted POST however, but just to try it out) then got rid of the service code and simply made this call, the file is fine:
window.open(
${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet, "_blank");
So, really, what is the difference here? Why does the same or very similar code work for Angular1 but not Angular2??
Please tell us about your environment: Windows 10, Web Storem and Visual Studio, Nuget and NPM
-
Angular version: 2.0.X 2.2.1
-
Browser: all
I’m sorry to not produce a plunkr here. Hopefully, you can help determine if this is a bug or not. I seriously think it is.
Thank you,
Karen Healey
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 39 (4 by maintainers)
@healkar01 I had the same issue and I resolved using native angular2 http request in this way:
Backend:
(Note: excel is a binary Buffer)
Angular2 Service:
Angular2 Component:
@mgarcs Thanks! Your answer should be up-voted more!
I didn’t notice the difference at first. In short, the OP mistake was using
responseType
as a header. Instead one should use it inget
options (second argument, type: RequestOptionsArgs). like:this.http.get(url , { responseType: ResponseContentType.Blob })
.The service looks fine but the component is wrong. You already have a blob (your data is a blob) and you are creating another blob from the current blob.
I have simply this:
Karen
I am a relatively new developer.
Thank you all for your contribution. The following works in Chrome but not in IE 11: var link = document.createElement(‘a’); link.href = window.URL.createObjectURL(blob); link.download = file.fileName; link.click();
My HttpReponseMessage looks like this: HttpResponseMessage response = new HttpResponseMessage(); response.Content = new ByteArrayContent(buffer); response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(“application/vnd.openxmlformats-officedocument.spreadsheetml.sheet”); response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue(“attachment”); response.StatusCode = HttpStatusCode.OK; return response;
Thanks.
I struggled with this for hours and nothing worked except adding the one line above:
this.http.get(url { responseType: ResponseContentType.Blob }
Is there another way to prompt the user to download the file?
//This doesn’t work
Hello again… I was able to make it work with native xhr. I’m happy with this solution, however I still believe that there is a bug in angular2.
Here are both methods. The first does not work:
This one works:
You can close this out if you feel you should, however I’m sure someone else will bump up against this one.
Thank you for the xhr suggestion!
Hard to say anything without repro 😦
@aulona2014 nothing wrong with your service, but the wrong is about how you renamed it.
you get a file with extension .docx from API, and you renamed it to .txt, that’s why you getting that’s text.
try to change the code above, to this or you can renamed it to name you to want, but with the original extension.
and you need open with the application support extension .docx, (like ms word, google docs)
this is an example, I’m getting the text similar to you because I’m open the extension .docx in notepad, where notepad is not supported open .docx file.
Thank may help.
I have to include FileSaver and it shows the icon to download or save the file. My backend is in spring
downloadFile(): Observable<Object[]> { return Observable.create(observer => {
}