C# 将文件从ASP.NET核心web api发布到另一个ASP.NET核心web api

C# 将文件从ASP.NET核心web api发布到另一个ASP.NET核心web api,c#,asp.net,asp.net-core,C#,Asp.net,Asp.net Core,我们正在构建一个web应用程序,它由Angular2前端、ASP.NET核心web api公共后端和ASP.NET核心web api专用后端组成 将文件从Angular2上传到公共后端works。但是我们更愿意将它们转发到私有后端 现行工作守则 [HttpPost] public StatusCodeResult Post(IFormFile file) { ... } 从那里,我可以使用file.CopyTo(fileStream)将文件保存到磁盘 但是,我希望将该文件或那些文件,或者理

我们正在构建一个web应用程序,它由Angular2前端、ASP.NET核心web api公共后端和ASP.NET核心web api专用后端组成

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

现行工作守则

[HttpPost]
public StatusCodeResult Post(IFormFile file)
{
  ...
}
从那里,我可以使用file.CopyTo(fileStream)将文件保存到磁盘

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

我不知道如何使用asp.net core的HttpClient类实现这一点

我试过各种各样的东西,比如

StreamContent ss = new StreamContent(HttpContext.Request.Body);
var result = client.PostAsync("api/Values", ss).Result;
但是我的第二个后端得到一个空文件

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


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

调用私有后端api时忽略HttpClient,是否可以从公共core api项目引用私有core api项目,并直接从core api项目调用控制器?请参阅请求仍然为null/空。如果请求带有一个值,那么问题在于HttpClient的使用

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

Solution 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);
            }                   
        }
    }          
}

嗨,我也有同样的问题,这就是我的工作原理:

我的设置是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();
}
要查看完整代码,请参见我的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;
        }
    }
公共异步任务发布(HttpContent)
{
使用(HttpClient HttpClient=new HttpClient())
{
HttpResponseMessage响应=等待httpClient.PostAsync(端点,内容);
if(response.StatusCode==HttpStatusCode.Created)
{
T result=JsonConvert.DeserializeObject(wait response.Content.ReadAsStringAsync());
返回新的RestResult{Result=Result,ResultCode=HttpStatusCode.OK};
}
RestResult非OK结果=
新RestResult{Result=default(T),ResultCode=response.StatusCode,Message=wait response.Content.ReadAsStringAsync()};
返回非OK结果;
}
}
//是的,我知道我没有得到HttpStatusCode.Created back;)


快乐编码;)

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 });
    }


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

因此,在我的帮助下,我得出了以下工作解决方案:

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字段以及多个附加文件,所有的数据都毫无问题地到达了我的后端服务器。

这个解决方案不是很好,因为你在公共后端的内存中加载了整个内容-想象一下,如果你的文件超过了RAM的容量……这是唯一适合我的解决方案@乔治·尼西莫夫:你知道另一种选择吗?似乎对我的网关文件传递很有效。感谢您提供的解决方案。无需转换为ByteArrayContent,您可以添加类似以下内容的StreamContent对象:multiContent.add(新的StreamContent(file.OpenReadStream(),“file”,file.FileName));你好我还想发送一个参数与此特别是为用户我上传这个文件。我需要随此请求发送userId。你也能帮忙吗?还有一件事。我如何使用postman来点击这个特定的上传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");
    }
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);