You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular + .NET 文件下载异常问题求助

问题

我有一个.NET文件下载接口,URL为https://localhost:7004/api/Files/download:

  • 直接在浏览器粘贴该URL可正常触发浏览器下载管理器,支持暂停/取消,但刷新页面不会重新触发下载;
  • 在Angular应用中调用该接口时,文件需完整下载完成后才会交给浏览器下载管理器,无法实时显示进度,调试发现subscribe回调仅在下载完成后触发。已尝试blob和arrayBuffer作为responseType,问题仍存在。

相关代码如下:

.NET端点代码

private readonly string _filePath = "C:/Users/shadi/Desktop/file.zip";

[HttpGet("download")]
public IActionResult DownloadFile()
{
    if (!System.IO.File.Exists(_filePath))
    {
        return NotFound();
    }

    var fileName = "file.zip";
    var fileStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read);
    var fileLength = new FileInfo(_filePath).Length;

    Response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}");
    Response.Headers.Add("Content-Length", fileLength.ToString());
    Response.Headers.Add("Content-Type", "application/octet-stream");

    return new FileStreamResult(fileStream, "application/octet-stream");
}

Angular服务代码

downloadFile(): Observable<Blob> {
    const url = `${this.filesUrl}/download`
    return this.http.get(url, { responseType: 'blob' });
}

Angular组件代码

download(): void {
    this.fileService.downloadFile().subscribe((res: Blob) => {
      const url = window.URL.createObjectURL(res);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'file.zip';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      window.URL.revokeObjectURL(url);
    });
}
解决方案

核心原因

Angular的HttpClient.get默认会等待整个响应完全接收后才触发订阅回调,无法实时获取下载进度。而浏览器直接访问接口时,服务器以流式返回内容,浏览器下载管理器能实时处理流式数据,所以能显示进度、支持暂停。

1. 修改Angular代码实现实时进度监听

要实现实时进度,需要启用reportProgress并将observe设置为'events',这样可以监听下载过程中的各类事件(包括进度事件):

修改后的Angular服务代码

import { HttpClient, HttpEvent, HttpEventType } from '@angular/common/http';
import { Observable } from 'rxjs';

// ...

downloadFile(): Observable<HttpEvent<Blob>> {
  const url = `${this.filesUrl}/download`;
  return this.http.get(url, {
    responseType: 'blob',
    reportProgress: true,
    observe: 'events'
  });
}

修改后的Angular组件代码

download(): void {
  let downloadedBytes = 0;
  let totalBytes = 0;

  this.fileService.downloadFile().subscribe({
    next: (event) => {
      // 处理进度事件,可用于更新UI
      if (event.type === HttpEventType.DownloadProgress) {
        totalBytes = event.total || 0;
        downloadedBytes = event.loaded;
        const progress = Math.round((downloadedBytes / totalBytes) * 100);
        console.log(`下载进度:${progress}%`);
      }
      // 下载完成,触发浏览器下载
      else if (event.type === HttpEventType.Response) {
        const blob = event.body;
        if (blob) {
          const url = window.URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = 'file.zip';
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          window.URL.revokeObjectURL(url);
        }
      }
    },
    error: (err) => {
      console.error('下载失败', err);
    }
  });
}

2. .NET代码优化(可选)

原.NET代码已经实现了流式返回,不过可以简化为使用PhysicalFile方法,它会自动处理文件流、Content-Length等响应头,代码更简洁:

private readonly string _filePath = "C:/Users/shadi/Desktop/file.zip";

[HttpGet("download")]
public IActionResult DownloadFile()
{
    if (!System.IO.File.Exists(_filePath))
    {
        return NotFound();
    }

    var fileName = "file.zip";
    // PhysicalFile会自动设置Content-Disposition、Content-Length、Content-Type
    return PhysicalFile(_filePath, "application/octet-stream", fileName);
}

额外说明

  • 浏览器直接访问时刷新页面不重新触发下载,是因为浏览器缓存了响应头,可在.NET接口中添加Response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");禁用缓存,刷新页面就会重新下载。
  • 若无需监听进度,只想直接触发浏览器下载管理器,可在Angular中直接打开新窗口访问下载URL:window.open(${this.filesUrl}/download, '_blank'),这种方式无需等待下载完成,直接交给浏览器处理。

内容的提问来源于stack exchange,提问作者Shadi Aslan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 22:02:13