2

(Jhipster, front-end Angular 9 & Backend spring-boot)

My app do a xls report. The report is done with Apache Poi and copy localy.

Now I'm trying to download the file to the client side, but I don't know how.

I want to delete the file when the download is done. It's a post method because I send the data for the report.

Do you have any idea?

Here's my Controller:

public void createFullReport(@Valid @RequestBody ReportDTO report, HttpServletResponse response) throws IOException {
    log.debug("REPORTDTO : {}", report);

    File outputFile = this.reportService.makeFullReport(report);

    log.debug("FILE EXIST:{}", outputFile.exists());
    log.debug("IS FILE:{}", outputFile.isFile());
    log.debug("FILE NAME:{}", outputFile.getName());

    FileInputStream stream = new FileInputStream(outputFile);
    response.setContentType("application/vnd.ms-excel");
    response.setHeader("Content-disposition", "attachment; filename=" + outputFile.getName());
    }

My service:

create(report: IReport): any {
    console.log(report);
    return this.http.post<any>(this.resourceUrl, report, { observe: 'response' });
  }

My component:

this.reportService.create(this.report).subscribe((response: any) => {
  console.log(response);
  var blob = new Blob([response._body], { type: 'application/vnd.ms-excel' });
});

EDIT

controller:

@PostMapping("/report")
@PreAuthorize(
    "hasAnyAuthority(\"" +
    AuthoritiesConstants.ADMIN +
    "\"+\"," +
    AuthoritiesConstants.CUSTOMER_ADMIN +
    "\"+\"," +
    AuthoritiesConstants.INSPECTOR +
    "\")"
)
public ResponseEntity  createFullReport(@Valid @RequestBody ReportDTO report, HttpServletResponse response) throws IOException {
    log.debug("REPORTDTO : {}", report);
    XSSFWorkbook wb = (XSSFWorkbook) this.reportService.makeFullReport(report);
    response.setHeader("Content-Disposition","attachment; filename=\"timesheet.xlsx\"");
    writeToOutputStream(response,wb);
    return ResponseEntity.ok().build();
}

private void writeToOutputStream(HttpServletResponse response,XSSFWorkbook wb){

    ServletOutputStream out ;
    try {
        out = response.getOutputStream();
        wb.write(out);
        wb.close();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Angular service:

  create(report: IReport): any {
    console.log(report);
    let HTTPOptions:Object = {

      headers: new HttpHeaders({
          'Content-Type': 'application/json'
      }),
      responseType: 'blob'
   }
    return this.http.post<any>(this.resourceUrl, report,HTTPOptions);
  }
Girbouillle
  • 73
  • 1
  • 13

1 Answers1

4

I want to delete the file when the download is done. It's a post method because I send the data for the report.

That would be complex and you don't need so much complexity.

Try not saving your xls file somewhere. Just create it with your apache poi as a workbook. Then write the contents of that workbook directly in your controller as a byte array.

With that you will achieve creating and delivering a xls file on the fly without the need to synchronize backend and frontend for removing it later.

 @PostMapping()
 public ResponseEntity createAndDeliverFile(HttpServletResponse response){
        
        response.setHeader("Content-Disposition","attachment; filename=\"myFileName.xlsx\"");

        XSSFWorkbook wb = fileWriterService.createAndReturnFile();

        writeToOutputStream(response,wb);

        return ResponseEntity.ok().build();
    }


   public void writeToOutputStream(HttpServletResponse response,XSSFWorkbook wb){

    ServletOutputStream out ;
    try {
        out = response.getOutputStream();
        wb.write(out);
        wb.close();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

As for downloading the file your code does not seem wrong however it can be that you have slightly to adjust something if it brings any kind of error. Just add FileSaver to download the file when ready

 My component:

    import * as FileSaver from 'file-saver';
    
    this.reportService.create(this.report).subscribe((response: any) => {
      console.log(response);
      var blob = new Blob([response._body], { type: 'application/vnd.ms-excel'});
      FileSaver.saveAs(blob, 'filename' + '.xlsx;);
    });

Edit after comments (It seems that conversion to Blob when data come from xlsx need extra care!)

create(report: IReport): any {
    console.log(report);
    let HTTPOptions:Object = {

      headers: new HttpHeaders({
          'Content-Type': 'application/json'
      }),
      responseType: 'blob' as 'json'   <----------
   }
    return this.http.post<any>(this.resourceUrl, report,HTTPOptions);
  }

My component:

    import * as FileSaver from 'file-saver';
    
    this.reportService.create(this.report).subscribe((response: any) => {
    console.log(response);
    var blob = new Blob([s2ab(atob(data))], {type: ''}); <----------
    FileSaver.saveAs(blob, 'filename' + '.xlsx;); 
    });

   private s2ab(s) {
     var buf = new ArrayBuffer(s.length);
     var view = new Uint8Array(buf);
     for (var i=0; i!=s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
     return buf;
    }*

Also don't forget to send credits to this answer if it works out blob from xlsx

Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
  • Thank you, when I try this I have a 200 error: SyntaxError: Unexpected token P in JSON. Is there something to do for the front-end too? – Girbouillle Jan 27 '21 at 14:46
  • Check the json that you send in the backend in your service the create method. check console.log(report). Something from the JSON that you send in the backend is wrong. Also remove observe: response . As I think it more I can see that this observe may be causing the error – Panagiotis Bougioukos Jan 27 '21 at 14:52
  • That's weird, before this code I don't had any error. I removed the observe:response but it doesn't change anything : /. When I comment the writeOutputStream, the error disappears but it isn't the solution ^^ – Girbouillle Jan 27 '21 at 15:07
  • I add this => let HTTPOptions:Object = { headers: new HttpHeaders({ 'Content-Type': 'application/json' }), responseType: 'blob' } And now it downloads but the file is empty. – Girbouillle Jan 27 '21 at 15:34
  • I compare the size of the document for the client and in local and it's not the same. For the client it is an empty page (9bytes), but the local report is full and his size is bigger(5,09 KB). Do you have any idea? – Girbouillle Jan 27 '21 at 15:47
  • When I open a file with notePad there's just "undefined" and the console.log() show this => Blob {size: 5219, type: "text/xml"} – Girbouillle Jan 27 '21 at 15:51
  • can you try in options the following -> responseType: 'blob' as 'json' – Panagiotis Bougioukos Jan 27 '21 at 16:21
  • check my edited answer and apply those changes – Panagiotis Bougioukos Jan 27 '21 at 16:26
  • I tried but I have a new error => ERROR DOMException: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded. – Girbouillle Jan 27 '21 at 21:47
  • new Blob [s2ab(atob(data))]. Remove atob and try with new Blob [s2ab(data)] – Panagiotis Bougioukos Jan 28 '21 at 00:03
  • I have this issue now => ERROR TypeError: s.charCodeAt is not a function at FullReportComponent.s2ab. But I still have this response : size: 5220, type: "text/xml". – Girbouillle Jan 28 '21 at 09:09