1

I would like to export an Excel file via a JAX-RS webservice and then download / open it from a web application. It sounds it works whether it is called from curl but not called from an AngularJS v1.4.7 front end. I am using Microsoft Excel 2013 64 bits under Windows 7 Professional.

On the server side the webservice is implemented as below:

@POST
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Path("/compare")
public Response compare(
        @FormDataParam("controlFile") InputStream controlIs,
        @FormDataParam("controlFile") FormDataContentDisposition controlFileDetail,
        @FormDataParam("testFile") InputStream testIs,
        @FormDataParam("testFile") FormDataContentDisposition testFileDetail
) {
    //(...)
    //creating my Excel file using Apache POI SXSSFWorkbook
    File file = new File(reportFileName);
    ResponseBuilder responseBuilder = Response.ok((Object) file, MediaType.APPLICATION_OCTET_STREAM)
                    .header("Access-Control-Allow-Origin", "*")
                    .header("Content-Disposition", "attachment; filename=test.xlsx");
    return responseBuilder.build();
}

I can query the webservice via curl and obtain the expected result

curl -X POST -F "controlFile=@loremIpsum1.xml" -F "testFile=@loremIpsum2.xml" http://localhost:8080/xmlcompare-rs/xmlcompare/compare > comparison.xls

For information without redirection in a file the exported format looks like:

 åì⌐H            ♂   _rels/.rels╡Æ┴j├0♀å∩y
ú{π┤â1F£^╩á╖1║╨l%1I,c{[·÷≤vZJ[
c║      ²· ☼ñz;Oú°á►-;♣δ▓☻AN│▒«S≡zxZ=Çê ¥┴æ↔)8RämS╘/4b╩;▒╖>èlΓóé>% (e╘=M↑K÷Σ≥ñσ0a╩mΦñG=`GrSU≈2ⁿ÷Ǫ►B,|┼▐{│å∩╤▓♫↑:J
(...)

I am now working to call the exact same webservice from an AngularJS front end wherein the webservice call is as below:

angular.module('compareWwwApp')
          .service('compareService', ['$http', function ($http) {
          this.xmlcompare = function(controlFile, testFile){
            var fd = new FormData();
            fd.append('controlFile', controlFile);
            fd.append('testFile', testFile);
            var xmlcompareUrl='http://localhost:8080/xmlcompare-rs/xmlcompare/compare';
            $http.post(xmlcompareUrl, fd, {
                transformRequest: angular.identity,
                headers: {'Content-Type': undefined}
              })
            .success(function(result){
              var blob = new Blob([result], {
                type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
              });
              var objectUrl = URL.createObjectURL(blob);
              window.open(objectUrl);
            })
            .error(function(){
            });
  }
}]);

The webservice sounds executed server side as expected (from the logs) and it returns an xlsx document shown in the browser.

enter image description here

Clicking on the downloaded doc, Excel opens and tells me "We found a problem with some content in XXX.xlsx. Do you want us to try to recover as mych as we can?(..)" and then "Excel cannot open the file XXX.xlsx because the file format or file extension is not valid. Verify that the file has not been corrupted and that file extension matches the format"

I did a few attempts switching to type 'application/vnd.ms-excel' or returning a StreamingOutput instead of a MediaType.APPLICATION_OCTET_STREAM object as depicted here but without success.

Any advice ?

Thank you,

Community
  • 1
  • 1

3 Answers3

2

You need to add responseType: 'arraybuffer' to your POST call. Then everything will be ok.

So it will be

$http.post(xmlcompareUrl, fd, {
  transformRequest: angular.identity,
  headers: {'Content-Type': undefined},
  responseType: 'arraybuffer'
})
k10der
  • 716
  • 1
  • 10
  • 15
  • You are welcome. Had the same issue a year ago dealing with Excel file generation on the server and saving it with angular. ) – k10der May 09 '16 at 11:35
1
JS
----
window.open("http://localhost:8080/xmlcompare-rs/xmlcompare/compare");

Rest
------
    @GET
    @Path("excelmisreport")
    @Produces("application/vnd.ms-excel")    
    public Response getExcelReport(){
    ResponseBuilder response = Response.ok((Object) file);
    response.header("Content-Disposition",
    "attachment; filename=MISReport.xls");      
    return response.build();
}

No need multiform data
-1

You can try removing the following lines from your code:

.success(function(result){
var blob = new Blob([result], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
var objectUrl = URL.createObjectURL(blob);
window.open(objectUrl);
})
.error(function(){
});

Just invoking the API should return a proper response which the content-disposition header set and hence, the browser should be able to invoke the "Download/Save File" dialog.

Venkat
  • 1,095
  • 2
  • 13
  • 25
  • That is wrong for XHR. It works, if we just click some anchor tag with appropriate href, or submitting form via ordinary POST and not XHR. – k10der May 09 '16 at 10:22