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.