Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.3k views
in Technique[技术] by (71.8m points)

angular - Angular2 download excel file from Web API, file is corrupt

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. 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.

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??

-- Karen

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I know that others have found the same problem. I have resolved it, but had to switch to xhr to make it work.

This first method is the one that does NOT work. I simplified it somewhat from above:

    generateMonthlySpreadsheet2(searchCriteria: Object) {
        let headers = new Headers();
        headers.append('Content-Type', 'application/json');
        headers.append('responseType', 'blob');

        return this._http.post(`${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, {headers: headers})
            .map(response => {
                if (response.status == 400) {
                    this.handleError;
                } else if (response.status == 200) {
                    var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                    var blob = new Blob([(<any>response)._body], { type: contentType });            // size is 89KB instead of 52KB
//                    var blob = new Blob([(<any>response).arrayBuffer()], { type: contentType });  // size is 98KB instead of 52KB
//                    var blob = new Blob([(<any>response).blob()], { type: contentType });         // received Error: The request body isn't either a blob or an array buffer
                    return blob;
                }
            })
            .catch(this.handleError);
    }

This second method is the one that DOES work:

generateMonthlySpreadsheet(searchCriteria: Object): Observable<Object[]> {
    return Observable.create(observer => {

        let xhr = new XMLHttpRequest();

        xhr.open('POST', `${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, true);
        xhr.setRequestHeader('Content-type', 'application/json');
        xhr.responseType='blob';

        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {

                    var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
                    var blob = new Blob([xhr.response], { type: contentType });
                    observer.next(blob);
                    observer.complete();
                } else {
                    observer.error(xhr.response);
                }
            }
        }
        xhr.send();

    });
}

Hopefully this will help others! I saw this issue posted elsewhere so I will add a link to my solution there as well.

-- Karen


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...