Set File Name while downloading via blob in Angular 5
Asked Answered
C

4

21

Below is my Typescript code to download file From API

DownloadLM() {
var ID= sessionStorage.getItem("UserID");
    return this.http.get(this.baseurl + 'api/DownloadFiles/DownloadLM/' + ID,
      {
        headers: {
          'Content-Type': 'application/json'
        },
        responseType: 'arraybuffer'
      }
    )
      .subscribe(respData => {
        this.downLoad(respData, this.type);
      }, error => {
      });
  }

  downLoad(data: any, type: string) {
    var blob = new Blob([data], { type: type.toString() });
    var url = window.URL.createObjectURL(blob);
    var pwa = window.open(url);
    if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') {
      alert('Please disable your Pop-up blocker and try again.');
    }
  }

This is Fine to download Excel File , but it gives a random name to file which I don't want , I want to set file name of my choice when downloading it ,

Where can I set file name here ? any property of Blob ?

Cyrenaic answered 22/8, 2018 at 5:6 Comment(0)
S
44

You can set the download attribute to the filename you want, set the href to the object url, and then just call click

var blob = new Blob([data], { type: type.toString() });
var url = window.URL.createObjectURL(blob);
var anchor = document.createElement("a");
anchor.download = "myfile.txt";
anchor.href = url;
anchor.click();
Super answered 22/8, 2018 at 5:8 Comment(4)
Thanks , it works but I am also checking if pop up is blocked in browser or not , for that I wrote var pwa = anchor.click(); if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') { alert('Please disable your Pop-up blocker and try again.'); } , but in your code it is not possible to check it as anchor.click() does not return a windowCyrenaic
you might need to do a hack there, upvote if this helpedSuper
as it is happening in JS based framework and JS can be disabled , so taking caring of that is also required (pop up enable message), It partly helped not fullyCyrenaic
As it throws "Property 'download' does not exist on type HtmlAnchorElement" setting it as anchor.setAttribute("download", "<fileName>"); worksRudder
M
22

If you want the exact filename of the uploaded file, set a custom header of the filename from backed API stream.

You can use it like this: my Excel API response headers:

content-disposition: inline;filename="salesReport.xls" 
content-type: application/octet-stream 
date: Wed, 22 Aug 2018 06:47:28 GMT 
expires: 0 
file-name: salesReport.xls 
pragma: no-cache 
transfer-encoding: chunked 
x-application-context: application:8080 
x-content-type-options: nosniff 
x-xss-protection: 1; mode=block

Service.ts

excel(data: any) {
  return this.httpClient.post(this.config.domain + 
  `/api/registration/excel/download`,data, {observe: 'response', responseType: 'blob'})
  .map((res) => {
      let data = {
                     image: new Blob([res.body], {type: res.headers.get('Content-Type')}),
                     filename: res.headers.get('File-Name')
                  }
    return data ;
  }).catch((err) => {
    return Observable.throw(err);
  });
}

Component.ts

excelDownload (data) {
   this.registration.excel(data).subscribe(
    (res) => {
     const element = document.createElement('a');
      element.href = URL.createObjectURL(res.image);
      element.download = res.filename;
      document.body.appendChild(element);
      element.click();
     this.toastr.success("Excel generated  successfully");
    },
  (error) =>{
     this.toastr.error('Data Not Found');
  });
}
Monostome answered 22/8, 2018 at 6:13 Comment(1)
res.headers.get('File-Name') helped. tnx.Glowworm
G
3

Since some asked for a version with promise so you can use await und async:

Part 1: Get the Blob From the server:

  generateSapExcel(data: GenerateSapExport): Promise<HttpResponse<Blob>> {
    return this.http.post(`${this.pathprefix}/GenerateSapExcel`, data, { responseType: 'blob', observe: 'response' })
      .toPromise()
      .catch((error) => this.handleError(error));
  }

Part 2: Extract HttpResponse and deliver it to the user:

public downloadFile(data: HttpResponse<Blob>) {
    const contentDisposition = data.headers.get('content-disposition');
    const filename = this.getFilenameFromContentDisposition(contentDisposition);
    const blob = data.body;
    const url = window.URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.download = filename;
    anchor.href = url;
    anchor.click();
  }

  private getFilenameFromContentDisposition(contentDisposition: string): string {
    const regex = /filename=(?<filename>[^,;]+);/g;
    const match = regex.exec(contentDisposition);
    const filename = match.groups.filename;
    return filename;
  }

Part 3: Combine both:

      const blobresponse = await this.dataService.generateSapExcel(dataToSend);
      this.downloadService.downloadFile(blobresponse);

Part 4: server:

        [HttpPost]
        [Route(nameof(GenerateSapExcel))]
        public async Task<FileStreamResult> GenerateSapExcel(GenerateSapExportDto dto)
        {
            Stream stream = await _sapKurepoService.GenerateSapExcel(dto);
            FileStreamResult result = File(stream, FileHelper.ContentypeExcel, "excel.xlsx");
            return result;
        }
Glowworm answered 11/10, 2019 at 18:58 Comment(0)
G
0

I personally found that using the File type rather than Blob worked quite smoothly in this case. I was able to name the file and also avoid creating an anchor tag that must be clicked.

Below would be your updated download() function. Having the file type of 'application/octet-stream' was necessary in indicating to window.open() that the URL should be used as a download and not a browser display.

download(data: any) {
    let file = new File([data], 'file.json', { type: 'application/octet-stream' });
    let url = window.URL.createObjectURL(file);
    var pwa = window.open(url);
    if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') {
      alert('Please disable your Pop-up blocker and try again.');
    }
}

Hope this helps!

Goodwill answered 30/8, 2023 at 19:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.