将文件从ASP.NET Core Web API发布到另一个ASP.NET Core Web API

25

我们正在构建一个 Web 应用程序,其中包括 Angular2 前端、ASP.NET Core Web API 公共后端和 ASP.NET Core Web API 私有后端。

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

目前的工作代码:

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

接着,我可以使用 file.CopyTo(fileStream) 将文件保存到磁盘中。

但是,我希望重新发送该文件或这些文件,或者最理想的情况是将整个请求发送到我的第二个Web API Core。

我不确定如何使用 asp.net core 的 HttpClient 类来实现此操作。

我已经尝试了各种方法,例如:

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

但是我的第二个后端获取的是一个空的IFormFile。

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

解决方案必须使用两个Web API Core。

5个回答

29

解决方案

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

4
这个解决方案不太好,因为你要在公共后端将整个东西加载到内存中——想象一下,如果你有一个文件超过了你的 RAM 可以容纳的大小... - Yuriy Anisimov
1
这似乎是我唯一有效的解决方案。@GeorgeAnisimov 您知道有其他替代方案吗? - eVolve
1
不需要转换为ByteArrayContent,您可以添加StreamContent对象,例如: multiContent.Add(new StreamContent(file.OpenReadStream(), "file", file.FileName)); - Victor SDK
嗨。我还想为上传此文件的特定用户发送一个参数。我需要在此请求中发送userId。你能帮忙吗?还有一件事。我如何使用Postman来访问这个特定的上传API?它的主体应该是什么? - Sweetie
谢谢,这帮助我更好地理解了 MultipartFormDataContent 的使用。 - Sandeep Nandey
显示剩余2条评论

15

你好,我遇到了相同的问题,以下是解决方法:

我的设置是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返回;)

编码愉快 ;)


2
"API Code" 翻译为 "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 });
    }

Back End

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

最初的回答:

下载源代码


1
我曾经遇到类似的情况——我需要一个代理方法来转发文件和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字段和多个附加文件的多部分表单数据进行了复杂请求的测试,所有数据都成功传输到我的后端服务器。


0

在调用私有后端API时忽略HttpClient,你能否从公共Core API项目中引用私有Core API项目并直接从Core API项目中调用控制器?请注意请求仍然为空/无效。如果请求返回一个值,则问题出在使用HttpClient上。

理想情况下,您希望为要分发给使用客户的私有Core API创建一个包库(类似于SDK)。这就像是一个封装器/代理。这样,您可以隔离私有后端系统,并在隔离中进行故障排除。因此,您的公共Core API项目(即私有后端客户端)可以将其作为nuget包引用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接