【问题标题】:Post files from ASP.NET Core web api to another ASP.NET Core web api将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api
【发布时间】:2017-01-16 18:15:26
【问题描述】:

我们正在构建一个由 Angular2 前端、一个 ASP.NET Core Web api 公共后端和一个 ASP.NET Core Web api 私有后端组成的 Web 应用程序。

将文件从 Angular2 上传到公共后端是可行的。但我们更愿意将它们转发到私有后端。

当前工作代码

[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
  ...
}

从那里我可以使用 file.CopyTo(fileStream); 将文件保存到磁盘

但是,我想重新发送该文件或那些文件,或者理想情况下,将整个请求重新发送到我的第二个 Web api 核心。

我不确定如何使用 asp.net 核心的 HttpClient 类来实现这一点。

我尝试过各种各样的事情,例如

StreamContent ss = new StreamContent(HttpContext.Request.Body);
var result = client.PostAsync("api/Values", ss).Result;

但是我的第二个后端得到了一个空的 IFormFile。

我感觉可以将文件作为流发送并在另一端重建它们,但无法使其正常工作。

解决方案必须使用两个 web api 核心。

【问题讨论】:

    标签: c# asp.net asp.net-core


    【解决方案1】:

    解决方案

    DMZ 中的公共后端

    [HttpPost]
    public StatusCodeResult Post(IFormFile file)
    {
        try
        {
            if (file != null && file.Length > 0)
            {
                using (var client = new HttpClient())
                {
                    try
                    {
                        client.BaseAddress = new Uri(currentPrivateBackendAddress);
                        
                        byte[] data;
                        using (var br = new BinaryReader(file.OpenReadStream()))
                            data = br.ReadBytes((int)file.OpenReadStream().Length);
    
                        ByteArrayContent bytes = new ByteArrayContent(data);
    
                        
                        MultipartFormDataContent multiContent = new MultipartFormDataContent();
                        
                        multiContent.Add(bytes, "file", file.FileName);
    
                        var result = client.PostAsync("api/Values", multiContent).Result;
                        
    
                        return StatusCode((int)result.StatusCode); //201 Created the request has been fulfilled, resulting in the creation of a new resource.
                                                    
                    }
                    catch (Exception)
                    {
                        return StatusCode(500); // 500 is generic server error
                    }
                }
            }
    
            return StatusCode(400); // 400 is bad request
    
        }
        catch (Exception)
        {
            return StatusCode(500); // 500 is generic server error
        }
    }
    

    私人后端

    [HttpPost]
    public void Post()
    {
        //Stream bodyStream = HttpContext.Request.Body;
    
        if (Request.HasFormContentType)
        {
            var form = Request.Form;
            foreach (var formFile in form.Files)
            {
                var targetDirectory = Path.Combine(_appEnvironment.WebRootPath, "uploads");
    
                var fileName = GetFileName(formFile);
    
                var savePath = Path.Combine(targetDirectory, fileName);
    
                using (var fileStream = new FileStream(savePath, FileMode.Create))
                {
                    formFile.CopyTo(fileStream);
                }                   
            }
        }          
    }
    

    【讨论】:

    • 这个解决方案不是很好,因为您将整个内容加载到公共后端的内存中 - 想象一下,如果您的文件超出 RAM 可以容纳的范围......
    • 这是唯一对我有用的解决方案。 @GeorgeAnisimov 你知道替代方案吗?
    • 似乎很适合我的网关文件通过。感谢您的解决方案。
    • 不需要转换为 ByteArrayContent,您可以添加 StreamContent 对象,例如:multiContent.Add(new StreamContent(file.OpenReadStream(), "file", file.FileName));
    • 嗨。我还想发送一个带有此参数的参数,特别是对于我正在上传此文件的用户。我需要用这个请求发送 userId。你也可以帮忙吗?还有一件事。我如何使用邮递员来点击这个特定的上传 api?它的身体会是什么?
    【解决方案2】:

    您好,我遇到了同样的问题,这对我有用:

    我的设置是 netCore MVC netCoreApi。

    我的 MVC 控制器看起来像:

    [HttpPost("UploadFiles")]
    public async Task<IActionResult> Post(List<IFormFile> files)
    {
        Sp4RestClient dataPovider = new Sp4RestClient("http://localhost:60077/");
    
        long size = files.Sum(f => f.Length);
    
        foreach (var file in files)
        {
           await dataPovider.ImportFile(file);
        }
    
        return Ok();
    }
    

    数据提供者方法:

    public async Task ImportFile(IFormFile file)
        {
            RestClient restClient = new RestClient(_queryBulder.BuildImportFileRequest());
    
            using (var content = new MultipartFormDataContent())
            {
                content.Add(new StreamContent(file.OpenReadStream())
                {
                    Headers =
                    {
                        ContentLength = file.Length,
                        ContentType = new MediaTypeHeaderValue(file.ContentType)
                    }
                }, "File", "FileImport");
    
                var response = await restClient.Post<IFormFile>(content);
            }
        }
    

    至少我的 WebApi 控制器:

    [HttpPost]
    [Route("ImportData")]
    public IActionResult Import(IFormFile file)
    {         
        return Ok();
    }
    

    在这里查看完整代码是我的 RestClient Post 方法:

    public async Task<RestResult<T>> Post<T>(HttpContent content)
        {
            using (HttpClient httpClient = new HttpClient())
            {
                HttpResponseMessage response = await httpClient.PostAsync(Endpoint, content);
                if (response.StatusCode == HttpStatusCode.Created)
                {
                    T result = JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
                    return new RestResult<T> { Result = result, ResultCode = HttpStatusCode.OK };
                }
                RestResult<T> nonOkResult =
                    new RestResult<T> { Result = default(T), ResultCode = response.StatusCode, Message = await response.Content.ReadAsStringAsync() };
                return nonOkResult;
            }
        }
    

    // 是的,我知道我没有得到 HttpStatusCode.Created ;)

    快乐编码 ;)

    【讨论】:

      【解决方案3】:

      API 代码

       [Route("api/upload/{id}")]
          [HttpPost]
          public async Task<IActionResult> Post(string id)
          {
              var filePath = @"D:\" + id; //+ Guid.NewGuid() + ".png";
              if (Request.HasFormContentType)
              {
                  var form = Request.Form;
                  foreach (var formFile in form.Files)
                  {
                      if (formFile.Length > 0)
                      {
                          using (var stream = new FileStream(filePath, FileMode.Create))
                          {
                              await formFile.CopyToAsync(stream);
                          }
                      }
                  }
              }
              return Ok(new { Path = filePath });
          }
      

      后端

              [Route("home/UploadFile")]
          [HttpPost]
          public IActionResult UploadFile(IFormFile file)
          {
              if (file == null || file.Length == 0)
                  return Content("file not selected");
      
              var client = new HttpClient();
      
              byte[] data;
              using (var br = new BinaryReader(file.OpenReadStream()))
                  data = br.ReadBytes((int)file.OpenReadStream().Length);
              ByteArrayContent bytes = new ByteArrayContent(data);
              MultipartFormDataContent multiContent = new MultipartFormDataContent
              {
                  { bytes, "file", file.FileName }
              };
              var result = client.PostAsync("http://localhost:2821/api/upload/" + file.FileName, multiContent).Result;
              return RedirectToAction("file");
          }
      

      Download Source

      【讨论】:

        【解决方案4】:

        我也遇到过类似的情况——我需要一个代理方法来转发文件以及 JSON 数据等等。我不想对代理中的数据进行任何分析,让最终接收者处理它。

        因此,在@Anton Tykhyy 的帮助下,我找到了以下可行的解决方案:

        byte[] arr = null;
        using (var mems = new MemoryStream())
        {
            // read entire body into memory first because it might be chunked with unknown length
            await request.Body.CopyToAsync(mems);
            await mems.FlushAsync(); // not sure if needed after CopyToAsync - better safe then sorry
            arr = mems.ToArray();
        }
        
        msg.Content = new ByteArrayContent(arr);
        msg.Content.Headers.ContentLength = arr.Length;
        
        // keep content-type header "as is" to preserve multipart boundaries etc.
        msg.Content.Headers.TryAddWithoutValidation("Content-Type", request.ContentType);
        
        var response = await _httpClient.SendAsync(msg);
        
        
        

        我用复杂的请求对其进行了测试,该请求包含带有 JSON 字段和多个附件的多部分表单数据,并且所有数据都没有任何问题地到达我的后端服务器。

        【讨论】:

          【解决方案5】:

          调用私有后端API时忽略HttpClient,能否从公共Core API项目中引用私有Core API项目,直接从Core API项目中调用控制器?查看请求仍然为空/空。如果请求带有一个值,那么问题在于使用 HttpClient。

          理想情况下,您希望为您想要分发给消费客户端的私有核心 API 创建一个包库(一种 SDK)。这就像一个包装器/代理。通过这种方式,您可以隔离私有后端系统,并且可以单独对其进行故障排除。因此,您的公共核心 API 项目(即私有后端客户端)可以将其作为 nuget 包引用。

          【讨论】:

            猜你喜欢
            • 2018-02-28
            • 2020-04-16
            • 1970-01-01
            • 2018-03-15
            • 2021-07-07
            • 2019-12-28
            • 2017-07-05
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多