我想我已经掉进了兔子洞,并怀疑有一个比我正在尝试的解决方案容易得多的解决方案。基本上在我的 .net 核心 MVC 应用程序中,我想从外部 API 中提取流视频文件。
这意味着,对于对我的操作的任何请求,我想通过网络调用外部 API 函数。我需要向请求添加一个标头,但传递已请求的所有其他标头,然后一旦我收到 API 的回复,我就会将响应完整转发给消费者。它基本上就像没有状态代码的重定向,并向重定向的源添加标头。
我这样做的方式如下。在我的控制器中,我调用 ForwardedResponseResult:
var client = await this.FileAssetsApiContext.GetHttpClient();
//Copy the Headers
foreach (var header in Request.Headers)
{
header.Value.ToList().ForEach(p => {
if (client.DefaultRequestHeaders.Contains(header.Key))
{
client.DefaultRequestHeaders.Remove(header.Key);
client.DefaultRequestHeaders.Add(header.Key, p);
}
else
{
client.DefaultRequestHeaders.Add(header.Key, p);
}});
}
var fullPath = Url.Combine(VideosRootPath, path);
var response = await client.GetAsync(url);
return new ForwardedResponseResult(response);
然后我有一个转发类:
public class ForwardedResponseResult : ActionResult
{
public override void ExecuteResult(ActionContext context)
{
throw new NotImplementedException();
}
public ForwardedResponseResult(HttpResponseMessage httpResponseMessage)
{
this.HttpResponseMessage = httpResponseMessage;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
var responseToCopy = this.HttpResponseMessage;
var responseToCopyHeaders = responseToCopy.Headers.ToArray();
var responseToCopyContentHeaders = responseToCopy.Content.Headers.ToArray();
var streamToCopy = await responseToCopy.Content.ReadAsStreamAsync();
var responseToReturnTo = context.HttpContext.Response;
var streamToCopyTo = responseToReturnTo.Body;
var headers = responseToCopy.Headers.ToArray();
var contentHeaders = responseToCopy.Content.Headers.ToArray();
var headersTo = responseToReturnTo.Headers.ToArray();
foreach (var header in responseToCopy.Headers)
{
header.Value.ToList().ForEach(p => responseToReturnTo.Headers.Add(header.Key, p));
}
responseToReturnTo.StatusCode = (int) responseToCopy.StatusCode;
foreach (var header in responseToCopy.Content.Headers)
{
header.Value.ToList().ForEach(p => responseToReturnTo.Headers.Add(header.Key, p));
}
//Copy to the output buffer
var remainingBytes = streamToCopy.Length;
while (remainingBytes > 0)
{
//var bufferSize = 1024;
var bufferSize = 8096;
var buffLen = remainingBytes > bufferSize ? bufferSize : remainingBytes;
var buffer = new byte[buffLen];
streamToCopy.Read(buffer, 0, (int)buffLen);
streamToCopyTo.Write(buffer, 0, (int)buffLen);
remainingBytes -= buffLen;
}
//streamToCopy.CopyTo(streamToCopyTo);
//await streamToCopyTo.FlushAsync();
}
public HttpResponseMessage HttpResponseMessage { get; }
}
这似乎是巨大的矫枉过正,范围请求的性能很糟糕。我错过了一些明显的东西吗?有没有办法更简单地做到这一点?
我想从外部 API 拉取流视频文件。
在下载量很大的情况下,您希望确保使用 HttpCompletionOption.ResponseHeadersRead
.否则,GetAsync
调用实际上是将整个响应内容加载到内存中(并且.Content.ReadAsStreamAsync
实际上不是异步的(。
此外,您还可以使用现有的FileStreamResult
类型。这样的事情应该有效:
var client = await this.FileAssetsApiContext.GetHttpClient();
... // Copy Request.Headers to client.DefaultRequestHeaders.
var fullPath = Url.Combine(VideosRootPath, path);
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
var stream = await client.Content.ReadAsStreamAsync();
... // Copy response.Headers / response.Content.Headers to Response.Headers
return File(stream, "application/octet-stream");
我不知道复制请求/响应标头的干净方法,但它似乎是几种实用程序方法的好地方。