2

The Problem: the Open With / Save file dialog is not appearing until the POST request completes. The user will click the download link, then after X number of minutes (X increases with file size) the Open With / Save file dialog appears. I expected the dialog to appear immediately and the progress to be tracked immediately, rather than after the request completes.

The Question: How can I ensure the user is immediately presented with the Open With / Save dialog and the download progress is immediately displayed in the browser's "downloading" menu as opposed to after the POST request completes?

Client Code (Angular):

downloadFile(){
  this.documentApi.postDownloadFile(this.fileId).subscribe(data => {
    var link = document.createElement("a");
    link.href = window.URL.createObjectURL(data);
    link.download= this.fileName;
    link.dispatchEvent(new MouseEvent(`click`, {bubbles: true, cancelable: true, view: window}));
  });
}

postDownloadFile(id): Observable<any> {
  let httpOptions = {
    headers: new HttpHeaders({
      'Accept': 'text/html, application/xhtml+xml, */*',
      'Content-Type': 'application/octet-stream'
    }),
    responseType: 'blob' as 'blob'
  };
  return this.http.post(`${BaseUrl}reviewDocument/downloadFile`, id, httpOptions).pipe(
    catchError(this.handleError) 
  );
}

Java Code:

@RequestMapping(value="/reviewDocument/downloadFile", method = RequestMethod.POST, produces="application/octet-stream")
public void downloadFile(HttpServletResponse response, @RequestBody String fileId) throws IOException {
    try{

        File file = new File(getFilePath(fileId));

        response.setContentType("application/octet-stream");

        response.setHeader("Content-Disposition", String.format("attachment; filename=\"%s\"", file.getName()));

        response.setContentLength((int)file.length());

        InputStream inputStream = new FileInputStream(file); 

        int read=0;
        byte[] bytes = new byte[1024];
        OutputStream os = response.getOutputStream();

        while((read = inputStream.read(bytes))!= -1){
            os.write(bytes, 0, read);
        }
        os.flush();
        os.close(); 

    }
    catch(Exception ex){
        System.out.println(ex);
    }
}

I originally was returning the file like this: return ResponseEntity.ok().body(FileCopyUtils.copyToByteArray(inputStream)); and that caused out of memory errors when the file size was large.

The Out of Memory issue seems to have been resolved by using the Outputstream. I've also tried using IOUtils, but it seems to be the same thing as writing to the output stream and since I haven't run into any more Out of Memory errors, I haven't refactored again.

Regardless, whatever solution I've tried, the prompt to Open With / Save the file is delayed until the request completes entirely. Nothing shows up in the browsers "downloading" menu either. For images, and pdfs, etc., this isn't an issue because the file size is small so the request takes less time, but whenever a large file is being served this problem is noticeable because the request takes longer.

Researching other SO questions:

Angular file download from RestFul Java Spring backend - This person says he has a workaround, but doesn't provide it. I tried to experiment based on his answer, but had no luck.

Using HTML5/Javascript to generate and save a file - This just seems to address the issue of downloading client-side content.

There are numerous other examples, but save for the first link, which is a down-voted question, I cannot find anything specific to my issue.

Edit: Based on guidance from comments, I switched to GET from POST, but experienced the same behavior.

Jacob Barnes
  • 1,480
  • 1
  • 12
  • 29
  • 1
    Try using GET request. I have used similar code, with content disposition as attachment in spring boot. When you hit the GET request, new window quickly opens up and closes, showing a open or save modal box. Just try once – Mrunmay Deswandikar Mar 04 '19 at 16:37
  • 1
    In the frontend code, just update it to GET request, and enable GET in your controller if any restrictions are added – Mrunmay Deswandikar Mar 04 '19 at 16:38
  • 1
    I just changed over to using GET and still have the same experience: The dialog doesn't pop up until the GET request completes. – Jacob Barnes Mar 04 '19 at 16:55
  • 1
    I see, let me have some check. I dont thike file size could be cause of this issue. Will get back to you – Mrunmay Deswandikar Mar 04 '19 at 16:56

0 Answers0