将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api
Posted
技术标签:
【中文标题】将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api【英文标题】:Post files from ASP.NET Core web api to another 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 核心。
【问题讨论】:
【参考方案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 包引用。
【讨论】:
以上是关于将文件从 ASP.NET Core Web api 发布到另一个 ASP.NET Core Web api的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 fetch 将 FormData 从 javascript 发送到 ASP.NET Core 2.1 Web API
在 ASP.NET MVC 5 控制器中使用 POST 从 dotnet Core Web API 下载文件
在 ASP.NET Core Web API 中一起接收文件和其他表单数据(基于边界的请求解析)
使用 ASP.NET Core Web API 将图像上传到 Cloudianary 时出现空文件错误