2

My problem is that I am getting the wrong sized file on the client side. Here is my @Controller ...

@RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public ResponseEntity<?> download(final HttpServletRequest request,
                                  final HttpServletResponse response,
                                  @PathVariable("id") final int id) throws IOException {
    try {
        // Pseudo-code for retrieving file from ID.
        Path zippath = getZipFile(id);

        if (!Files.exists(zippath)) {
            throw new IOException("File not found.");
        }

        ResponseEntity<InputStreamResource> result;
        return ResponseEntity.ok()
                             .contentLength(Files.size(zippath))
                             .contentType(MediaType.APPLICATION_OCTET_STREAM)
                             .body(new InputStreamResource(new FileInputStream(zippath.toFile())));
    } catch (Exception ex) {
        // ErrorInfo is another class, unimportant
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ErrorInfo(ex));
    }
}

... and here is my client-side code using angular-file-saver ...

$http({url: "export/download/" + exportitem.exportId, withCredentials: true})
.then(function(response) {
    function str2bytes(str) {
        var bytes = new Uint8Array(str.length);
        for (var i=0; i<str.length; i++) {
            bytes[i] = str.charCodeAt(i);
        }
        return bytes;
    }
    var blob = new Blob([str2bytes(response.data)], {type: 'application/octet-stream'});
    FileSaver.saveAs(blob, "download.zip");
}, $exceptionHandler);

The original file is 935673 bytes but response.data is 900728 and passing it through the transformation to Uint8Array results in a Blob that is 900728 in size as well. Either way, the resulting saved file is 900728 bytes (34945 bytes shy). Also it is not quite the same in what gets written. It seems to slightly get bloated but then the last part just seems to be truncated. Any ideas what I might be doing wrong?

UPDATE

I just updated my controller method to be the following and got the exact same result. Grrr.

@RequestMapping(value = "/download/{id}", method = RequestMethod.GET)
public void download(final HttpServletRequest request,
                     final HttpServletResponse response,
                     @PathVariable("id") final int id) throws IOException {
    // Pseudo-code for retrieving file from ID.
    Path zippath = getZipFile(id);

    if (!Files.exists(zippath)) {
        throw new IOException("File not found.");
    }

    response.setContentType("application/zip");
    response.setHeader("Content-Disposition",
                        "attachment; filename=download.zip");

    InputStream inputStream = new FileInputStream(zippath.toFile());
    org.apache.commons.io.IOUtils.copy(inputStream, response.getOutputStream());
    response.flushBuffer();
    inputStream.close();
}
2
  • what happens in zippath.toFile()? Commented Jan 13, 2016 at 4:31
  • Turns a Path object into a File object so that I can use it in FileInputStream. Commented Jan 13, 2016 at 17:24

3 Answers 3

4

So the problem turned out to be angular's $http service. I also tried jQuery's ajax method. Both gave the same result. If I instead use the native XMLHttpRequest it works correctly. So the Java code was sound. I first verified this by exposing the file directly to the internet and then both using curl and directly accessing in the browser I managed to download the file of the correct size. Then I found this solution so that I could also download the file via javascript.

var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.withCredentials = true;
xhr.onreadystatechange = function (){
    if (xhr.readyState === 4) {
        var blob = xhr.response;
        FileSaver.saveAs(blob, filename);
    }
};
xhr.send();

Why does angular or jQuery give the wrong result? I still don't know but if anyone wishes to give an answer that uses those it would be appreciated.

Sign up to request clarification or add additional context in comments.

1 Comment

same thing. No idea why jquery/angular were increasing the file size and corrupting the data.
2

responseType: blob did the trick for a zip file

Angular 2 +

this.http.get('http://localhost:8080/export', { responseType: ResponseContentType.Blob })
  .subscribe((res: any) => {
        const blob = new Blob([res._body], { type: 'application/zip' });
         saveAs(blob, "fileName.zip");

Comments

0

i just stumbled over the 'responseType' in $http requests, you are probably looking for 'blob': https://docs.angularjs.org/api/ng/service/$http#usage

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.