如何使用 Angular2 或更高版本下载文件

Posted

技术标签:

【中文标题】如何使用 Angular2 或更高版本下载文件【英文标题】:How do I download a file with Angular2 or greater 【发布时间】:2016-05-10 08:52:50 【问题描述】:

我有一个 WebApi / MVC 应用程序,我正在为其开发一个 angular2 客户端(以替换 MVC)。我在理解 Angular 如何保存文件时遇到了一些麻烦。

请求没问题(与 MVC 一起工作正常,我们可以记录接收到的数据)但我不知道如何保存下载的数据(我主要遵循与 this post 相同的逻辑)。我确信它非常简单,但到目前为止我还没有掌握它。

组件函数的代码如下。我尝试了不同的替代方案,据我所知,blob 方式应该是可行的方式,但URL 中没有函数createObjectURL。我什至在窗口中找不到URL 的定义,但显然它存在。如果我使用 FileSaver.js module 我会得到同样的错误。所以我想这是最近发生变化或尚未实施的事情。如何触发A2中的文件保存?

downloadfile(type: string)

    let thefile = ;
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(data => thefile = new Blob([data],  type: "application/octet-stream" ), //console.log(data),
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    let url = window.URL.createObjectURL(thefile);
    window.open(url);

为了完整起见,获取数据的服务如下,但它唯一做的就是发出请求,如果成功则不映射传递数据:

downloadfile(runname: string, type: string)
   return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .catch(this.logAndPassOn);

【问题讨论】:

这种方法不能下载大文件。您将达到每个选项卡的内存限制。这可能低至 1-2GB。 @MatthewB。希望你说什么更好。 对于大文件下载,您需要指定一个新选项卡,例如如果模拟 点击,target 需要等于 "_blank" 或者做一个表单提交。我认为没有一种干净的方法可以解决 Ajax 样式请求的大文件大小限制。 【参考方案1】:

问题是 observable 在另一个上下文中运行,所以当你尝试创建 URL var 时,你有一个空对象,而不是你想要的 blob。

解决此问题的众多方法之一如下:

this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
                 error => console.log('Error downloading the file.'),
                 () => console.info('OK');

当请求准备好时,它会调用函数“downloadFile”,定义如下:

downloadFile(data: Response) 
  const blob = new Blob([data],  type: 'text/csv' );
  const url= window.URL.createObjectURL(blob);
  window.open(url);

blob 已完美创建,因此 URL var,如果未打开新窗口,请检查您是否已导入 'rxjs/Rx' ;

import 'rxjs/Rx' ;

希望对你有帮助。

【讨论】:

什么是this._reportService.getReport(),它返回什么? @Burjua getReport() 返回一个this.http.get(PriceConf.download.url) 我遇到的问题是窗口会立即打开和关闭而不是下载文件 这里如何设置文件名?默认情况下,它选择一个数值作为名称 我已使用上述代码从 API 响应下载文件,但在创建 Blob 部分时遇到一些错误“类型响应不可分配给类型 Blobpart”。如果有人知道这个问题,请提供帮助【参考方案2】:

试试this!

1 - 安装显示保存/打开文件弹出窗口的依赖项

npm install file-saver --save
npm install -D @types/file-saver

2- 使用此功能创建服务以接收数据

downloadFile(id): Observable<Blob> 
    let options = new RequestOptions(responseType: ResponseContentType.Blob );
    return this.http.get(this._baseUrl + '/' + id, options)
        .map(res => res.blob())
        .catch(this.handleError)

3- 在组件中使用 'file-saver' 解析 blob

import saveAs as importedSaveAs from "file-saver";

  this.myService.downloadFile(this.id).subscribe(blob => 
            importedSaveAs(blob, this.fileName);
        
    )

这对我有用!

【讨论】:

我将第 2 步与@Alejandro 的答案结合使用,无需安装文件保护程序即可工作... 谢谢!它完美地工作!我想知道我们是否可以获得响应标头上定义的文件名。这可能吗? error Av5 类型“RequestOptions”的参数不可分配给类型“ headers?: HttpHeaders | [标题:字符串]:字符串 |细绳[]; ; 这个不适合大文件下载。 @jfajunior 如果您不想从响应中获取文件名,则需要访问响应标头:将observe: 'response' 添加到请求选项并返回Observable&lt;HttpResponse&lt;Blob&gt;&gt; 而不是Observable&lt;Blob&gt; -现在您可以访问响应标头并读取文件名,例如res.headers.get('File-Name')【参考方案3】:

如果您不需要在请求中添加标头,要在 Angular2 中下载文件,您可以执行 simple (KISS PRINCIPLE):

window.location.href='http://example.com/myuri/report?param=x';

在您的组件中。

【讨论】:

有人能说出为什么这个答案被否决了吗?主题是使用 angular2 下载文件。如果此方法适用于进行简单下载,那么它也应该被标记为有效答案。 @SaurabhShetty,如果您想发送自定义标头,这将无济于事,例如,如果您想发送身份验证令牌怎么办?如果您查看 OP 问题,您可以看到他使用 authHttp! 我确实理解反对意见,但这个答案解决了我的问题。 如果让服务器在某些上下文中返回 url,服务器可以准备 url。例如:对象:MyRecord.Cover。封面可以是服务器中图像的 url。调用 get(Myrecord) 时,您让服务器返回准备好的 url (Cover),并设置安全令牌和其他标头。 这是一个有效的答案。只是因为它没有 不会使它不是答案。【参考方案4】:

这适用于寻找如何使用 HttpClient 和文件保护程序的人:

    安装文件保护程序

npm install file-saver --save

npm install @types/file-saver --save

API 服务类:

export() 
    return this.http.get(this.download_endpoint, 
        responseType: 'blob');

组件:

import  saveAs  from 'file-saver';
exportPdf() 
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));

【讨论】:

下载开始时如何在浏览器中显示文件大小?我将文件大小作为 http 标头中的内容长度发送。【参考方案5】:

这个怎么样?

this.http.get(targetUrl,responseType:ResponseContentType.Blob)
        .catch((err)=>return [do yourself])
        .subscribe((res:Response)=>
          var a = document.createElement("a");
          a.href = URL.createObjectURL(res.blob());
          a.download = fileName;
          // start download
          a.click();
        )

我可以做到。 不需要额外的包。

【讨论】:

如此简单,但它却完美无瑕。它不会弄乱 DOM,也不会创建任何元素。我将此解决方案与上述一些解决方案结合起来,它就像一个魅力。 with responseType: "blob" 我没有使用 res.blob(),它已经可以工作了!【参考方案6】:

对于较新的 Angular 版本:

npm install file-saver --save
npm install @types/file-saver --save


import saveAs from 'file-saver';

this.http.get('endpoint/', responseType: "blob", headers: 'Accept': 'application/pdf')
  .subscribe(blob => 
    saveAs(blob, 'download.pdf');
  );

【讨论】:

谢谢,使用 Angular 8。不知道为什么这么难找到。【参考方案7】:

正如Alejandro Corredor 所提到的,这是一个简单的范围错误。 subscribe 是异步运行的,open 必须放在该上下文中,以便在我们触发下载时完成数据加载。

也就是说,有两种方法可以做到这一点。正如文档所建议的,该服务负责获取和映射数据:

//On the service:
downloadfile(runname: string, type: string)
  var headers = new Headers();
  headers.append('responseType', 'arraybuffer');
  return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .map(res => new Blob([res], type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ))
            .catch(this.logAndPassOn);

然后,在组件上我们只是订阅并处理映射的数据。有两种可能性。 第一个,如原帖中所建议的,但需要 Alejandro 指出的小修正:

//On the component
downloadfile(type: string)
  this.pservice.downloadfile(this.rundata.name, type)
      .subscribe(data => window.open(window.URL.createObjectURL(data)),
                  error => console.log("Error downloading the file."),
                  () => console.log('Completed file download.'));
  

第二种方法是使用 FileReader。逻辑相同,但我们可以显式等待 FileReader 加载数据,避免嵌套,解决异步问题。

//On the component using FileReader
downloadfile(type: string)
    var reader = new FileReader();
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(res => reader.readAsDataURL(res), 
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    reader.onloadend = function (e) 
        window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
  

注意:我正在尝试下载 Excel 文件,即使触发了下载(所以这回答了问题),但文件已损坏。 See the answer to this post 避免损坏的文件。

【讨论】:

我认为文件损坏的原因是因为您将 res 加载到 blob 中,而您实际上想要 res._body 。然而_body 是一个私有变量并且不可访问。截至今天,http 响应对象上的 .blob().arrayBuffer() 尚未在 Angular 2 中实现。text()json() 是仅有的两个选项,但两者都会使你的身体出现乱码。你找到解决办法了吗? 嗨@rll,我按照上述步骤完成了订阅。我仍然看不到文件被下载。我也看不到任何错误。请帮忙 这两个选项让我下载文件,但它首先在后台加载数据。如果我有一个必须下载的大文件怎么办? 我的解决方案是使用&lt;a href=""&gt;&lt;/a&gt; 下载文件。 我知道这是一个旧答案,但它在搜索结果中排名靠前并且是公认的答案:`headers.append('responseType', 'arraybuffer');` 行是错误的。这是一个选项,而不是标题。请修复它。 Aaaand... 标头已创建但未使用。没有帮助。【参考方案8】:

下载 Angular 2.4.x 的 *.zip 解决方案:您必须从 '@angular/http' 导入 ResponseContentType 并将 responseType 更改为 ResponseContentType.ArrayBuffer(默认为 ResponseContentType.Json)

getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> 
 let headers = this.setHeaders(
      'Content-Type': 'application/zip',
      'Accept': 'application/zip'
    );

 return this.http.get(`$environment.apiUrl$path`,  
   headers: headers, 
   search: params, 
   responseType: ResponseContentType.ArrayBuffer //magic
 )
          .catch(this.formatErrors)
          .map((res:Response) => res['_body']);

【讨论】:

【参考方案9】:

通过 ajax 下载文件总是一个痛苦的过程,在我看来最好让服务器和浏览器来做这个内容类型协商的工作。

我觉得最好有

<a href="api/sample/download"></a> 

去做。这甚至不需要打开任何新窗口之类的东西。

示例中的 MVC 控制器可能如下所示:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()

    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");

【讨论】:

您是对的,但是您如何管理单页应用程序中的服务器错误?如果发生错误,通常 REST 服务会返回带有错误的 JSON,从而导致应用程序在另一个浏览器窗口中打开 JSON,这不是用户想要看到的 如果你有一个访问令牌,你需要提供这不起作用 这很简单。但是,如果您想做一些身份验证,那么就有可能拥有类似一次性令牌的东西。因此,您可以将 url 设置为:example.com/myuri/report?tokenid=1234-1233 并验证数据库中的令牌 ID,而不是这样。当然,这不是一个简单的场景并且适用于所有情况,但可以作为一种解决方案,您可以在将报告作为流返回之前访问数据库.. 从服务器获取下载地址。因此服务器可以使用一次性安全令牌准备 url。【参考方案10】:

我正在使用 Angular 4 和 4.3 httpClient 对象。我修改了在 Js 的技术博客中找到的答案,它创建了一个链接对象,使用它进行下载,然后销毁它。

客户:

doDownload(id: number, contentType: string) 
    return this.http
        .get(this.downloadUrl + id.toString(),  headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' )


downloadFile(id: number, contentType: string, filename:string)  

    return this.doDownload(id, contentType).subscribe(  
        res =>  
            var url = window.URL.createObjectURL(res);
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove(); // remove the element
        , error => 
            console.log('download error:', JSON.stringify(error));
        , () => 
            console.log('Completed file download.')
        ); 

 

this.downloadUrl 的值之前已设置为指向 api。我正在使用它来下载附件,所以我知道 id、contentType 和文件名: 我正在使用 MVC api 返回文件:

 [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
    public FileContentResult GetAttachment(Int32 attachmentID)
     
        Attachment AT = filerep.GetAttachment(attachmentID);            
        if (AT != null)
        
            return new FileContentResult(AT.FileBytes, AT.ContentType);  
        
        else
         
            return null;
         
     

附件类如下所示:

 public class Attachment
  
    public Int32 AttachmentID  get; set; 
    public string FileName  get; set; 
    public byte[] FileBytes  get; set; 
    public string ContentType  get; set;  

filerep 存储库从数据库中返回文件。

希望这可以帮助某人:)

【讨论】:

【参考方案11】:

如果你尝试调用你内部的新方法会更好subscribe

this._reportService.getReport()
    .subscribe((data: any) => 
        this.downloadFile(data);
    ,
        (error: any) => сonsole.log(error),
        () => console.log('Complete')
    );

downloadFile(data)函数内部我们需要创建block, link, href and file name

downloadFile(data: any, type: number, name: string) 
    const blob = new Blob([data], type: 'text/csv');
    const dataURL = window.URL.createObjectURL(blob);

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) 
      window.navigator.msSaveOrOpenBlob(blob);
      return;
    

    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'export file.csv';
    link.click();

    setTimeout(() => 

      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(dataURL);
      , 100);
    

【讨论】:

【参考方案12】:

对于那些使用 Redux 模式的人

我在文件保护程序中添加了@Hector Cuevas 在他的回答中命名的名称。使用 Angular2 v. 2.3.1,我不需要添加 @types/file-saver。

以下示例是将期刊下载为 PDF。

日志操作

public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action 
 return 
   type: JournalActions.DOWNLOAD_JOURNALS,
   payload:  referenceId: referenceId 
 ;


public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action 
 return 
   type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
   payload:  blob: blob 
 ;

期刊效应

@Effect() download$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS)
    .switchMap((payload) =>
        this._journalApiService.downloadJournal(payload.referenceId)
        .map((blob) => this._actions.downloadJournalsSuccess(blob))
        .catch((err) => handleError(err, this._actions.downloadJournalsFail(err)))
    );

@Effect() downloadJournalSuccess$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
    .map((payload) => saveBlobAs(payload.blob, 'journal.pdf'))

日记服务

public downloadJournal(referenceId: string): Observable<any> 
    const url = `$this._config.momentumApi/api/journals/$referenceId/download`;
    return this._http.getBlob(url);

HTTP 服务

public getBlob = (url: string): Observable<any> => 
    return this.request(
        method: RequestMethod.Get,
        url: url,
        responseType: ResponseContentType.Blob
    );
;

日志减速器 虽然这只设置了我们应用程序中使用的正确状态,但我仍然想添加它以显示完整的模式。

case JournalActions.DOWNLOAD_JOURNALS: 
  return Object.assign(, state, <IJournalState> downloading: true, hasValidationErrors: false, errors: [] );


case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: 
  return Object.assign(, state, <IJournalState> downloading: false, hasValidationErrors: false, errors: [] );

我希望这会有所帮助。

【讨论】:

【参考方案13】:

我分享对我有帮助的解决方案(非常感谢任何改进)

在您的服务“pservice”上:

getMyFileFromBackend(typeName: string): Observable<any>
    let param = new URLSearchParams();
    param.set('type', typeName);
    // setting 'responseType: 2' tells angular that you are loading an arraybuffer
    return this.http.get(http://MYSITE/API/FILEIMPORT, search: params, responseType: 2)
            .map(res => res.text())
            .catch((error:any) => Observable.throw(error || 'Server error'));

组件部分:

downloadfile(type: string)
   this.pservice.getMyFileFromBackend(typename).subscribe(
                    res => this.extractData(res),
                    (error:any) => Observable.throw(error || 'Server error')
                );


extractData(res: string)
    // transforme response to blob
    let myBlob: Blob = new Blob([res], type: 'application/vnd.oasis.opendocument.spreadsheet'); // replace the type by whatever type is your response

    var fileURL = URL.createObjectURL(myBlob);
    // Cross your fingers at this point and pray whatever you're used to pray
    window.open(fileURL);

在组件部分,您调用服务而不订阅响应。订阅 有关 openOffice mime 类型的完整列表,请参阅:http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

【讨论】:

【参考方案14】:

要下载和显示 PDF 文件,一个非常相似的代码如下所示:

  private downloadFile(data: Response): void 
    let blob = new Blob([data.blob()],  type: "application/pdf" );
    let url = window.URL.createObjectURL(blob);
    window.open(url);
  

  public showFile(fileEndpointPath: string): void 
    let reqOpt: RequestOptions = this.getAcmOptions();  //  getAcmOptions is our helper method. Change this line according to request headers you need.
    reqOpt.responseType = ResponseContentType.Blob;
    this.http
      .get(fileEndpointPath, reqOpt)
      .subscribe(
        data => this.downloadFile(data),
        error => alert("Error downloading file!"),
        () => console.log("OK!")
      );
  

【讨论】:

【参考方案15】:

这是我在我的案例中所做的 -

// service method
downloadFiles(vendorName, fileName) 
    return this.http.get(this.appconstants.filesDownloadUrl,  params:  vendorName: vendorName, fileName: fileName , responseType: 'arraybuffer' ).map((res: ArrayBuffer) =>  return res; )
        .catch((error: any) => _throw('Server error: ' + error));


// a controller function which actually downloads the file
saveData(data, fileName) 
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    let blob = new Blob([data],  type: "octet/stream" ),
        url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);


// a controller function to be called on requesting a download
downloadFiles() 
    this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
        () => console.info("OK"));

解决方案引用自 - here

【讨论】:

【参考方案16】:

好吧,我写了一段受上述许多答案启发的代码,这些代码应该很容易在服务器发送带有内容处置标头的文件的大多数情况下工作,没有任何第三方安装,除了 rxjs 和 angular。

首先,如何从你的组件文件中调用代码

this.httpclient.get(
   `$myBackend`,
   
      observe: 'response',
      responseType: 'blob'
   
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));

如您所见,它基本上是来自 Angular 的平均后端调用,有两个变化

    我观察的是响应而不是正文 我明确表示响应是一个 blob

从服务器获取文件后,原则上我会将保存文件的整个任务委托给辅助函数,我将其保存在单独的文件中,然后导入到我需要的任何组件中

export const SaveFileResponse = 
(response: HttpResponse<Blob>, 
 filename: string = null) => 

    //null-checks, just because :P
    if (response == null || response.body == null)
        return;

    let serverProvidesName: boolean = true;
    if (filename != null)
        serverProvidesName = false;

    //assuming the header is something like
    //content-disposition: attachment; filename=TestDownload.xlsx; filename*=UTF-8''TestDownload.xlsx
    if (serverProvidesName)
        try 
            let f: string = response.headers.get('content-disposition').split(';')[1];
            if (f.includes('filename='))
                filename = f.substring(10);
        
        catch  
    SaveFile(response.body, filename);


//Create an anchor element, attach file to it, and
//programmatically click it. 
export const SaveFile = (blobfile: Blob, filename: string = null) => 
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blobfile);
    a.download = filename;
    a.click();

那里,不再有神秘的 GUID 文件名!我们可以使用服务器提供的任何名称,而不必在客户端显式指定它,或者覆盖服务器提供的文件名(如本例所示)。 此外,如果需要,可以轻松更改从内容配置中提取文件名的算法以满足他们的需要,并且其他所有内容都不会受到影响 - 如果在提取过程中出现错误,它只会传递“null”作为文件名。

正如另一个答案已经指出的那样,IE 需要像往常一样进行一些特殊处理。但是随着几个月后 Chromium Edge 的出现,我在构建新应用程序时不会担心这一点(希望如此)。 还有撤销 URL 的问题,但我对此不太确定,所以如果有人可以在 cmets 中帮助解决这个问题,那就太棒了。

【讨论】:

【参考方案17】:

到目前为止,我发现答案缺乏洞察力和警告。您可以而且应该注意与 IE10+ 的不兼容性(如果您关心的话)。

这是一个完整的例子,后面是应用程序部分和服务部分。请注意,我们设置了 observe: "response" 来捕获文件名的标题。另请注意,Content-Disposition 标头必须由服务器设置和公开,否则当前的 Angular HttpClient 将不会传递它。我在下面添加了一段 dotnet core 代码。

public exportAsExcelFile(dataId: InputData) 
    return this.http.get(this.apiUrl + `event/export/$event.id`, 
        responseType: "blob",
        observe: "response"
    ).pipe(
        tap(response => 
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        )
    );


private downloadFile(data: Blob, filename: string) 
    const blob = new Blob([data], type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;');
    if (navigator.msSaveBlob)  // IE 10+
        navigator.msSaveBlob(blob, filename);
     else 
        const link = document.createElement('a');
        if (link.download !== undefined) 
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        
    


private parseFilename(contentDisposition): string 
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;

Dotnet 核心,具有 Content-Disposition 和 MediaType

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    
        if (excelOutput != null)
        
            ContentDisposition contentDisposition = new ContentDisposition
            
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            ;
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        
        else
        
            throw new UserFriendlyException("The excel output was empty due to no events.");
        
    

【讨论】:

【参考方案18】:

在步骤 2 中使用文件保护程序和 HttpClient 更新 Hector 的答案:

public downloadFile(file: File): Observable<Blob> 
    return this.http.get(file.fullPath, responseType: 'blob')

【讨论】:

【参考方案19】:

我得到了一个解决方案,可以从 angular 2 下载而不会损坏, 使用 spring mvc 和 angular 2

1st- 我的返回类型是 :-ResponseEntity 来自 java 端。这里我发送的 byte[] 数组有来自控制器的返回类型。

第二个-将文件保护程序包含在您的工作区中-在索引页面中:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

3rd- 在组件 ts 上写下这段代码:

import ResponseContentType from '@angular.core';

let headers = new Headers( 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' );
        let options = new RequestOptions( headers: headers, responseType: ResponseContentType.Blob );
            this.http
            .post('/project/test/export',
                    somevalue,options)
              .subscribe(data => 

                  var mediaType = 'application/vnd.ms-excel';
                  let blob: Blob = data.blob();
                    window['saveAs'](blob, 'sample.xls');

                );

这将为您提供 xls 文件格式。如果您想要其他格式,请使用正确的扩展名更改媒体类型和文件名。

【讨论】:

【参考方案20】:

我今天也遇到了同样的情况,我不得不下载一个 pdf 文件作为附件(该文件不应该在浏览器中呈现,而是下载)。为了实现这一点,我发现我必须在 Angular Blob 中获取文件,同时在响应中添加 Content-Disposition 标头。

这是我能得到的最简单的(Angular 7):

服务内部:

getFile(id: String): Observable<HttpResponse<Blob>> 
  return this.http.get(`./file/$id`, responseType: 'blob', observe: 'response');

然后,当我需要在组件中下载文件时,我可以简单地:

fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);

更新:

从服务中删除了不必要的标头设置

【讨论】:

如果我使用 window.location.href 而不是 window.open Chrome 会将其视为多个文件下载。 如果您在标头中有身份验证令牌,这将不起作用 如果您使用随机名称保存文件,您可以允许下载 url 的安全性。通过从下载 url 中删除安全性,将实现下载速度的提高。【参考方案21】:

以下代码对我有用

把 HTML 做成这样:

<button type="button" onclick="startDownload(someData)">Click to download!</button>

JS如下:

let someData = ;
someData.name = 'someName';
someData.fileurl= 'someUrl';

function startDownload(someData)
    let link = document.createElement('a');
    link.href = someData.fileurl; //data is object received as response
    link.download = someData.fileurl.substr(someData.fileurl.lastIndexOf('/') + 1);
    link.click();

【讨论】:

html 部分是什么样的? 嗨@ScipioAfricanus,也用HTML代码改变了答案。 我试过了,文件似乎只是在浏览器中打开了。有没有办法让它启动下载窗口?【参考方案22】:
 let headers = new Headers(
                'Content-Type': 'application/json',
                'MyApp-Application': 'AppName',
                'Accept': 'application/vnd.ms-excel'
            );
            let options = new RequestOptions(
                headers: headers,
                responseType: ResponseContentType.Blob
            );


this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
                .subscribe(data => 
                    if (navigator.appVersion.toString().indexOf('.NET') > 0)
                    window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");

                    else 
                        var a = document.createElement("a");
                        a.href = URL.createObjectURL(data.blob());
                        a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
                        a.click();
                    
                    this.ui_loader = false;
                    this.selectedexport = 0;
                , error => 
                    console.log(error.json());
                    this.ui_loader = false;
                    document.getElementById("exceptionerror").click();
                );

【讨论】:

【参考方案23】:

只需将url 设置为href,如下所示。

<a href="my_url">Download File</a>

【讨论】:

有效吗?我收到错误..." ERROR TypeError: "从脚本访问 'file:///Downloads/test.json' 被拒绝。"" 谢谢你能分享一下你的网址是什么样的吗?它是文件协议还是 http 或其他什么? 这是文件协议。【参考方案24】:

&lt;a href="my_url" download="myfilename"&gt;Download file&lt;/a&gt;

my_url 应该具有相同的来源,否则它将重定向到该位置

【讨论】:

是的,这适用于纯 html,不知道为什么要寻找其他复杂的东西 参考这个:- developer.mozilla.org/en-US/docs/Web/HTML/Element/a; w3schools.com/tags/att_a_download.asp【参考方案25】:

您也可以直接从您使用下载属性的模板下载文件,并且您可以从组件中提供一个属性值到[attr.href]。 这个简单的解决方案应该适用于大多数浏览器。

<a download [attr.href]="yourDownloadLink"></a>

参考:https://www.w3schools.com/tags/att_a_download.asp

【讨论】:

欢迎来到 SO!请检查我的(排版和语法)更正是否有帮助。【参考方案26】:

Angular 12 + ASP.NET 5 WEB API

您可以从服务器返回一个 Blob 对象并创建一个锚标记并将 href 属性设置为从该 Blob 创建的对象 URL。现在单击锚点将下载文件。您也可以设置文件名。

downloadFile(path: string): Observable<any> 
        return this._httpClient.post(`$environment.ApiRoot/accountVerification/downloadFile`,  path: path , 
            observe: 'response',
            responseType: 'blob'
        );
    

saveFile(path: string, fileName: string): void 
            this._accountApprovalsService.downloadFile(path).pipe(
                take(1)
            ).subscribe((resp) => 
                let downloadLink = document.createElement('a');
                downloadLink.href = window.URL.createObjectURL(resp.body);
                downloadLink.setAttribute('download', fileName);
                document.body.appendChild(downloadLink);
                downloadLink.click();
                downloadLink.remove();
            );
            
        

后端

[HttpPost]
[Authorize(Roles = "SystemAdmin, SystemUser")]
public async Task<IActionResult> DownloadFile(FilePath model)

    if (ModelState.IsValid)
    
        try
        
            var fileName = System.IO.Path.GetFileName(model.Path);
            var content = await System.IO.File.ReadAllBytesAsync(model.Path);
            new FileExtensionContentTypeProvider()
                .TryGetContentType(fileName, out string contentType);
            return File(content, contentType, fileName);
        
        catch
        
            return BadRequest();
        
    

    return BadRequest();


【讨论】:

【参考方案27】:

创建一个临时锚标记,然后使用 javascript 以编程方式单击它

async function downloadFile(fileName) 
    const url = document.getElementById("url").value

    const link = document.createElement('a');
    link.href = await toDataURL(url);
    link.setAttribute('download', fileName ? fileName : url.split('/').pop());
    link.setAttribute('target', 'blank');
    document.body.appendChild(link);
    link.click();



function toDataURL(url) 
    return fetch(url)
        .then((response) => 
            return response.blob();
        )
        .then((blob) => 
            return URL.createObjectURL(blob);
        );
<input id="url" value="https://images.pexels.com/photos/1741205/pexels-photo-1741205.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"/>
<button onclick="downloadFile('test')">Download</button>

【讨论】:

【参考方案28】:

如果你只是将参数发送到一个 URL,你可以这样做:

downloadfile(runname: string, type: string): string 
   return window.location.href = `$this.files_api + this.title +"/"+ runname + "/?file="+ type`;

在接收参数的服务中

【讨论】:

【参考方案29】:

This 回答建议您不能直接使用 AJAX 下载文件,主要是出于安全原因。所以我会描述一下我在这种情况下会做什么,

01.component.html 文件内的锚标记中添加href 属性,例如:-

<div>
       <a [href]="fileUrl" mat-raised-button (click)='getGenaratedLetterTemplate(element)'> GENARATE </a>
</div>

02. 在您的component.ts 中执行以下所有步骤以绕过安全级别并打开另存为弹出对话框,例如:-

import  environment  from 'environments/environment';
import  DomSanitizer  from '@angular/platform-browser';
export class ViewHrApprovalComponent implements OnInit 
private apiUrl = environment.apiUrl;
  fileUrl
 constructor(
    private sanitizer: DomSanitizer,
    private letterService: LetterService) 
getGenaratedLetterTemplate(letter) 

    this.data.getGenaratedLetterTemplate(letter.letterId).subscribe(
      // cannot download files directly with AJAX, primarily for security reasons);
    console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
  

注意:如果您收到状态码为 200 的错误“OK”,此答案将有效

【讨论】:

【参考方案30】:

如果一个选项卡在没有下载任何内容的情况下打开和关闭,我尝试使用模拟锚链接进行跟踪并且它有效。

downloadFile(x: any) 
var newBlob = new Blob([x],  type: "application/octet-stream" );

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) 
      window.navigator.msSaveOrOpenBlob(newBlob);
      return;
    

    // For other browsers: 
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);

    var link = document.createElement('a');
    link.href = data;
    link.download = "mapped.xlsx";
    // this is necessary as link.click() does not work on the latest firefox
    link.dispatchEvent(new MouseEvent('click',  bubbles: true, cancelable: true, view: window ));

    setTimeout(function () 
      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(data);
      link.remove();
    , 100);  

【讨论】:

以上是关于如何使用 Angular2 或更高版本下载文件的主要内容,如果未能解决你的问题,请参考以下文章

如何检测特定浏览器版本或更高版本(jQuery)[重复]

语义版本控制问题和 npm 5 或更高版本

如何使使用旧 Java 版本开发的应用程序在 jdk 9 或更高版本上运行? [复制]

如何在版本 66 或更高版本的 Google Chrome 自助服务终端应用中允许视频自动播放

不再支持源选项 5。使用 6 或更高版本的 Android Studio

如何确定 SQL Server 2008 或更高版本