在Angular 5中通过blob下载时设置文件名

19

以下是我的Typescript代码,用于从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.');
    }
  }

可以下载Excel文件,但是它会随机生成文件名,我希望在下载时能够设置自己想要的文件名。

我应该在哪里设置文件名?是Blob的属性吗?

4个回答

43

您可以将下载属性设置为所需的文件名,将 href 设置为对象 URL,然后只需调用 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();

谢谢,它可以工作。但我还要检查浏览器是否阻止了弹出窗口。为此,我编写了var pwa = anchor.click();如果(!pwa || pwa.closed || typeof pwa.closed == 'undefined') {alert('请禁用您的弹出窗口拦截器并重试');}但在您的代码中,无法检查它,因为anchor.click()不返回窗口对象。 - Tanwer
你可能需要在那里进行一些黑客攻击,如果有帮助请点赞。 - Sajeetharan
由于基于JS的框架和JS可以被禁用,因此需要考虑到这一点(弹出启用消息),尽管它在某种程度上有所帮助,但并不完全解决问题。 - Tanwer
1
由于它抛出了“类型HtmlAnchorElement上不存在属性'download'”,因此将其设置为anchor.setAttribute("download", "<fileName>");可以解决问题。 - Saravanan Sachi

21

如果您想获取上传文件的确切文件名,请从后端API流中设置自定义标题的文件名。

您可以像这样使用它:我的Excel API响应头:

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);
  });
}

组件.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');
  });
}

res.headers.get('File-Name') 有帮助。谢谢。 - yonexbat

2

由于一些人要求使用promise版本,因此您可以使用await和async:

第1部分:从服务器获取Blob:

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

第二部分:提取HttpResponse并将其传递给用户:
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;
  }

第三部分:合并两者:
      const blobresponse = await this.dataService.generateSapExcel(dataToSend);
      this.downloadService.downloadFile(blobresponse);

第四部分:服务器:
        [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;
        }

0
我个人发现,在这种情况下使用File类型而不是Blob类型效果非常顺畅。我能够为文件命名,同时避免创建必须点击的锚点标签。
以下是您更新后的download()函数。将文件类型设置为'application/octet-stream'对于告知window.open()该URL应该用作下载而不是浏览器显示是必要的。
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.');
    }
}

希望这有所帮助!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接