javaspring-mvcopenpdf

Spring MVC Controller Generates PDF but Doesn't Prompt for Download


I have the following Spring MVC method that generates a PDF using OpenPDF:

    @PostMapping(path = "/report/**")
    public ResponseEntity<byte[]> report(@RequestBody ReportRequest reportRequest) {
        
    
        // get the media
        try {
            ApiReport report = reportRegistry.getReport(reportRequest.getReportId());
            if (report == null) {
                throw new ApiReportException("Report not found");
            }
            
            byte[] reportBytes = report.processReport(reportRequest.getParameters());
            if (reportBytes == null || reportBytes.length == 0) {
                throw new ApiReportException("Report length was 0");
            }
            
            HttpHeaders headers = new HttpHeaders();
            headers.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.pdf");
            headers.setContentType(MediaType.APPLICATION_PDF);
            headers.setContentLength(reportBytes.length);
            headers.setCacheControl("must-revalidate, post-check=0, pre-check=0");

            ResponseEntity<byte[]> result = new ResponseEntity<>(reportBytes, headers, HttpStatus.OK);
            return result;
        } catch (ReportException e) {
            throw new ApiReportException(e.getMessage(), e);
        }

    }

The report is generated, and bytes are returned to the browser. What does NOT happen is that I'm not prompted to download the file, nor does an application open to handle the PDF.

This method is called from an Angular application:

    const response = this.$http.post(this.reportUrl, reportRequest, {headers:headers, responseType: "blob"});
    return response;

I'm probably just making a very dumb mistake here. If anyone can point it out I would appreciate it.

Edit: Response Headers.

Chrome Response Headers


Solution

  • Ah it's a POST submitted through javascript. The browser isn't going to download that. If you install the file-saver package you can invoke the download yourself.

    import { saveAs } from 'file-saver';
    
    ...
    
    http.post(this.reportUrl, reportRequest, {headers:headers, responseType: "blob"})
       .subscribe( (blob : Blob) => saveAs( blob, filename ) );
    

    Something like that. Maybe you can dig around in the response headers to get the filename. I leave that up to you.

    If installing file-saver is a no go. You can try and write raw javascript to approximate the same thing.

    Or you have the server save that report somewhere (disk, DB, etc), and return an ID, then use a get to retrieve said ID using a GET and the browser will open a download for it.