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

Angular前端处理.Net API返回FormFile响应的问题

问题描述

后端.Net API同时返回file文件对象和status状态对象,但前端接收时file被转换为普通JSON对象,无法正常下载。尝试设置responseType: 'blob'后,不仅问题未解决,还丢失了status返回值。需求是让API返回的FormFile在前端TypeScript中转为File类型,支持保存、下载、展示等操作。


后端相关代码

GetFormFile方法

private FormFile getFormFile(string FileName, string url)
{
    var client = new HttpClient();
    var request = new HttpRequestMessage(HttpMethod.Get, url);
    request.Headers.Add("accept", "*/*");
    var res = client.SendAsync(request).Result;
    var readResponse = res.Content.ReadAsByteArrayAsync().Result;
    var stream = new MemoryStream(readResponse);
    var file = new FormFile(stream, 0, readResponse.Length, "name", FileName)
    {
        Headers = new HeaderDictionary(),
        ContentType = "application/pdf"
    };
    return file ;
}

FileVal接口实现

public IActionResult FileVal()
{
    Dictionary<string, object> objDictionary = new Dictionary<string, object>();
    try
    {
        var form = Request.Form;
        var filesData = Request.Form.ToList();
        var SrNo= Request.Form["SrNo"].ToString();
        foreach (var formFile in form.Files)
        {
            var res = filesRepo.ValidateFile(formFile, SrNo);
            if (res.data["StatusCode"].ToString() == "Success")
            {
                return Ok(res);
            }
        }
        objDictionary.Add("Response", "No Files present");
        _response.Status = 0;
        _response.Message = "Success";
        _response.Data = objDictionary;
        return Ok(_response);
    }
    catch (Exception exception)
    {
        _logger.Error(exception.ToString());
        objDictionary.Add("Response", exception.Message);
        _response.Status = 1;
        _response.Message = "error";
        _response.Data = objDictionary;
        return Ok(_response);
    }
}

API返回示例

{
    "data": {
        "Status": [
            {
                "message": "Validation Failed ",
                "result": "error",
                "filename": "VerifiedATL_File.pdf"
            }
        ],
        "StatusCode": "Success Call"
    },
    "file": {
        "contentType": "application/pdf",
        "headers": {
            "Content-Type": [
                "application/pdf"
            ]
        },
        "length": 251147,
        "name": "name",
        "fileName": "VerifiedATL_File.pdf"
    }
}

前端代码

this.https.post(`${this.VALIDATE_File}`, formData)
    .subscribe(res => {
      console.log('Result - ', res);

      var statuses = res['data']['Status'];
      let filess = res['file'];
      const file = new File([filess], statuses[0].filename);
      console.log(typeof filess);
      this.filesArray[sub1.Dropzonecode] = file;
      this.statusArray[sub1.Dropzonecode] = statuses[0];
      // const link = document.createElement('a');
      // link.href = URL.createObjectURL(blob);
      // link.download = statuses[0].filename;
      // document.body.append(link);
      // link.click();
      // link.remove();
    });

解决方案

核心问题

当前后端把FormFile直接序列化为JSON返回,仅保留了文件元数据,丢失了关键的二进制内容,导致前端无法还原出有效文件。

后端调整方案(二选一)

方案1:拆分两个接口

  • 第一个接口:返回文件验证状态信息(无文件二进制)
  • 第二个接口:根据状态中的标识(如文件名/文件ID)单独返回文件二进制流,前端调用时设置responseType: 'blob'即可获取有效Blob。

方案2:单接口混合返回(状态放响应头,文件放响应体)

修改后端代码,将状态信息存入响应头,文件二进制作为响应体返回:

public IActionResult FileVal()
{
    try
    {
        var form = Request.Form;
        var SrNo= Request.Form["SrNo"].ToString();
        foreach (var formFile in form.Files)
        {
            var res = filesRepo.ValidateFile(formFile, SrNo);
            if (res.data["StatusCode"].ToString() == "Success")
            {
                // 将状态信息序列化后存入自定义响应头
                Response.Headers.Add("X-Status-Data", System.Text.Json.JsonSerializer.Serialize(res.data));
                // 返回文件流
                var targetFile = res.file as FormFile;
                return File(targetFile.OpenReadStream(), targetFile.ContentType, targetFile.FileName);
            }
        }
        // 无文件场景返回状态JSON
        _response.Status = 0;
        _response.Message = "Success";
        _response.Data = new Dictionary<string, object>{{"Response", "No Files present"}};
        return Ok(_response);
    }
    catch (Exception exception)
    {
        _logger.Error(exception.ToString());
        _response.Status = 1;
        _response.Message = "error";
        _response.Data = new Dictionary<string, object>{{"Response", exception.Message}};
        return Ok(_response);
    }
}

注意:跨域场景下,后端需在CORS配置中添加Access-Control-Expose-Headers: X-Status-Data,否则前端无法读取自定义响应头。

前端对应调整(适配方案2)

this.https.post(`${this.VALIDATE_File}`, formData, { 
    responseType: 'blob', 
    observe: 'response' // 需设置此参数才能获取完整响应头
})
.subscribe(response => {
    // 从响应头读取状态信息
    const statusDataStr = response.headers.get('X-Status-Data');
    let statuses = [];
    if (statusDataStr) {
        const statusData = JSON.parse(statusDataStr);
        statuses = statusData.Status;
    }

    // 将Blob转换为File并处理
    const blob = response.body;
    if (blob && statuses.length > 0) {
        const file = new File([blob], statuses[0].filename, { type: 'application/pdf' });
        this.filesArray[sub1.Dropzonecode] = file;
        this.statusArray[sub1.Dropzonecode] = statuses[0];
        
        // 下载示例
        const link = document.createElement('a');
        link.href = URL.createObjectURL(file);
        link.download = statuses[0].filename;
        document.body.append(link);
        link.click();
        link.remove();
        URL.revokeObjectURL(link.href); // 释放内存
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 05:10:56