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

.NET Core Web API调用第三方API后,Angular13无法获取.gz文件二进制数据

问题:.NET Core Web API返回HttpResponseMessage导致前端收到序列化JSON而非二进制数据

场景与代码实现

我实现了一个.NET Core Web API方法,调用第三方API获取.gz格式文件的二进制数据,返回给Angular 13前端。

.NET Core API服务代码

public async Task<HttpResponseMessage> DownloadGzippedFile(string segment, string folderPath, string fileName, string token)
{
  using (var httpClient = new System.Net.Http.HttpClient())
  {
          // NOTE: accept all media types
          httpClient.DefaultRequestHeaders.Accept.Clear();
                httpClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("*/*"));
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

                System.Net.Http.HttpResponseMessage response;
                try
                {
                    var fileUrl = $"{{uri}}member/file/download/1.0?segment=" + segment + "&folderPath=" + folderPath + "&filename=" + fileName;
                    response = await httpClient.GetAsync(fileUrl);
                    
                    string value = await response.Content.ReadAsStringAsync();
                    if (response.IsSuccessStatusCode)
                        return response;
                    else
                        throw new HttpRequestException(response.ReasonPhrase);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return null;
                }
  }
}

Angular 13代码

Service层

download(segment:string,folderPath:string,fileName:string) :Observable<Blob> {
    folderPath='/'+folderPath.slice(0,folderPath.lastIndexOf('/'));
    const url = 'http://localhost:22721/api/NseDownload/DownloadGzippedFile?segment='+segment+'&folderPath='+folderPath+'&fileName='+fileName+'&token='+localStorage.getItem("token");
    return this.http.get(url,{ responseType: 'blob'  });
  }

Component层

NOTE: import { saveAs } from "file-saver";

download(node:any){
this.db.download('CM',node.folderPath,node.name).subscribe(response=>{
saveAs(response, node.name) 
})
}

问题现象

前端未获取到二进制数据,反而收到序列化后的HttpResponseMessage JSON结构:

{"version":"1.1","content":{"headers":[{"key":"Content-Disposition","value":["attachment; filename=\"07765_C_member_missing_client_details.csv.gz\""]},{"key":"Expires","value":["Mon, 20 Feb 2023 10:19:19 GMT"]},{"key":"Content-Length","value":["39968"]}],"statusCode":200,"reasonPhrase":"OK","headers":[{"key":"Vary","value":["Origin","Access-Control-Request-Method","Access-Control-Request-Headers","Origin","Access-Control-Request-Method","Access-Control-Request-Headers"]},{"key":"X-Frame-Options","value":["SAMEORIGIN"]},{"key":"X-XSS-Protection","value":["1; mode=block"]},{"key":"X-Forwarded-Proto","value":["https"]},{"key":"X-Content-Type","value":["nosniff"]},{"key":"Strict-Transport-Security","value":["max-age=31536000; includeSubdomains; preload"]},{"key":"Cache-Control","value":["no-store, no-cache, max-age=0"]},{"key":"Pragma","value":["no-cache"]},{"key":"Date","value":["Mon, 20 Feb 2023 10:19:19 GMT"]},{"key":"Connection","value":["keep-alive"]},{"key":"Set-Cookie","value":["HttpOnly; Secure"]}],"trailingHeaders":[],"requestMessage":{"version":"1.1","versionPolicy":0,"content":null,"method":{"method":"GET"},"requestUri":"{uri}/member/file/download/1.0?segment=CM&folderPath=/Reports&filename=07765_details.csv.gz","headers":[{"key":"Accept","value":["*/*"]},{"key":"Authorization","value":["Bearer eyJhbGciOiJSUzI1NiJ9.eyJtZW1iZXJDNSwiaWF0IjoxNjc2ODg4MzA1LCJqdGkiOiIzY2UzMGJjMy0yY2M4LTRjZDEtOWIyMy1jOWY0ZDEwMDgxOTgifQ.Dfn-G4US36sO3F1ekNhQZWk6J8uXH_PBaQGGGwevsNMncZgk4zUUJaC1peFqO9mCBy1UFmyO9ngw-YAP_aEzNg"]},{"key":"traceparent","value":["00-b76ec4bd3d634170ef8bce99e991ce57-76cf6e6800e6d661-00"]}],"properties":{},"options":{}},"isSuccessStatusCode":true}

问题原因

  1. .NET Core Web API中直接返回HttpResponseMessage时,框架会自动将该对象序列化为JSON,而非透传原始二进制响应内容。
  2. 代码中冗余的await response.Content.ReadAsStringAsync()操作,会将二进制文件内容读取为字符串,破坏原始二进制数据结构。

解决方法

1. 修改.NET Core API方法,返回标准文件流

将返回类型改为Task<IActionResult>,使用.NET Core内置的File方法返回二进制流,同时保留第三方响应的关键头信息:

public async Task<IActionResult> DownloadGzippedFile(string segment, string folderPath, string fileName, string token)
{
    using (var httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*"));
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        try
        {
            var fileUrl = $"{{uri}}member/file/download/1.0?segment={segment}&folderPath={folderPath}&filename={fileName}";
            var response = await httpClient.GetAsync(fileUrl);
            
            if (!response.IsSuccessStatusCode)
            {
                throw new HttpRequestException(response.ReasonPhrase);
            }

            // 获取第三方响应的原始二进制流
            var contentStream = await response.Content.ReadAsStreamAsync();
            // 从响应头中提取文件名(优先用第三方返回的文件名)
            var contentDisposition = response.Content.Headers.ContentDisposition;
            var targetFileName = contentDisposition?.FileNameStar ?? contentDisposition?.FileName ?? fileName;

            // 返回二进制文件流,指定正确的MIME类型
            return File(contentStream, "application/gzip", targetFileName);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
        }
    }
}

2. 优化Angular代码(可选但推荐)

使用HttpParams构建查询参数,避免手动拼接URL的编码问题,同时添加错误处理:

修改后的Service代码

import { HttpClient, HttpParams } from '@angular/common/http';
import { catchError } from 'rxjs/operators';

download(segment: string, folderPath: string, fileName: string): Observable<Blob> {
  folderPath = '/' + folderPath.slice(0, folderPath.lastIndexOf('/'));
  const params = new HttpParams()
    .set('segment', segment)
    .set('folderPath', folderPath)
    .set('fileName', fileName)
    .set('token', localStorage.getItem("token") || '');

  return this.http.get('http://localhost:22721/api/NseDownload/DownloadGzippedFile', {
    responseType: 'blob',
    params
  }).pipe(
    catchError(error => {
      console.error('下载失败:', error);
      throw error;
    })
  );
}

修改后的Component代码

download(node: any){
  this.db.download('CM', node.folderPath, node.name).subscribe({
    next: (response) => {
      saveAs(response, node.name);
    },
    error: () => {
      alert('文件下载失败,请稍后重试');
    }
  });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 12:46:15