Rexam
Rexam

Reputation: 913

Angular Http Client File Download - File name from the response

I am trying to get a File from an api url. My objective is to show this file (that was previously uploaded) in an input file field with its name. At the moment I have created the following service function:

  getDocument(): Observable<Blob> {
    const request = this.documentUrl;
    return this.http.get(request, { responseType: 'blob' });
  }

And when I use it

    this.myService.getDocument().subscribe(
      response => {
        console.log(response);
      }

I get a Blob which by definition doesn't have a name. I have seen that I can convert it into a file and give it a name but this solution doesn't fit my necessities. Is there a way I can get a File from the backend instead or is there a way to reconstruct it from the Blob with the original name it had?

Upvotes: 5

Views: 10676

Answers (2)

Gianluca Alfieri
Gianluca Alfieri

Reputation: 1

Please make sure that the backend API sets the file name in the response header. Usually, the file name is included in content-disposition header.

  • And if you are using CORS make sure that you are exposing your header.

I found it thanks to this answer and also this that CORS policy don't allow Angular to see all the headers, I'm using Java as the backend and this was how I solved the issue:

Angular:

this.myService.downloadBlob(BlobID).subscribe(
      (response: HttpResponse<Blob>) => {
        //same code as andsilver
        // Extract content disposition header
        const contentDisposition = response.headers.get('content-disposition');
        // Rest of your code to extract filename using contentDisposition
        // Extract the file name
        const filename = contentDisposition
          .split(';')[1]
          .split('filename')[1]
          .split('=')[1]
          .trim();
        this.downloadBlob(new Blob([(response.body)], { type: 'text/plain' }), filename);
      }

Service Angular:

downloadBlob(BlobID: number): Observable<HttpResponse<Blob>> {
        return this.http.get(myUrl+'?BlobID='+BlobID, { observe: 'response', responseType: 'blob' });
    }

Server side (Java in my case):

@GetMapping("/downloadBlob")
    public ResponseEntity<byte[]> downloadBlob(@RequestParam("BlobID") BigDecimal BlobID) {
            
            BlobAndName blobAndName = service.getDocumento(BlobID);
            byte[] blobAsBytes = Utils.blob2ByteArray(blobAndName.getBlob());
            HttpHeaders head = new HttpHeaders();
            head.add("content-disposition", "attachment; filename=" + blobAndName.getName());
            ArrayList<String> exposedHead = new ArrayList<>();
            exposedHead.add("content-disposition");
            head.setAccessControlExposeHeaders(exposedHead);
            return ResponseEntity.ok().headers(head).body(blobAsBytes);
    }

I must say that due to security concerns from my colleagues (that were not explained to me, so I don't even know what they are), this is not the solution I implemented, I got the name and the blob in different methods as I had the chance to do so by recycling a previous one that already got me different information I needed and which now gives me the blob name too.

Upvotes: 0

andsilver
andsilver

Reputation: 5972

Please make sure that the backend API sets the file name in the response header. Usually, the file name is included in content-disposition header.

For example: it looks like content-disposition: attachment;filename=XYZ.csv.

In angular, You can extract it like below:

.subscribe((response: HttpResponse<Blob>) => {
  // Extract content disposition header
  const contentDisposition = response.headers.get('content-disposition');

  // Extract the file name
  const filename = contentDisposition
        .split(';')[1]
        .split('filename')[1]
        .split('=')[1]
        .trim()
        .match(/"([^"]+)"/)[1];
});

Upvotes: 5

Related Questions