在我的服务中,我们需要获得另一个服务创建的zip文件并返回它。
这是我的代码(这个问题的代码已经简化了):
[HttpGet("mediafiles/{id}")]
public async Task<IActionResult> DownloadMediaFiles(int id)
{
var fileIds = _myProvider.GetFileIdsForEntityId(id); // result be like "1,2,3,4"
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync($"http://file-service/bulk/{fileIds}");
var stream = await response.Content.ReadAsStreamAsync();
return File(stream, "application/octet-stream", "media_files.zip");
}使用id,我可以收集创建fileIds字符串和调用其他服务所需的信息。
下面是另一个服务上的api (针对这个问题简化了代码):
[HttpGet("bulk/{idList}")]
public async Task<IActionResult> DownloadBulk(string idList)
{
var ids = string.IsNullOrEmpty(idList) ? new List<int>() : idList.Split(',').Select(x => Convert.ToInt32(x));
using var memoryStream = new MemoryStream();
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
var index = archive.CreateEntry("hello.txt");
using (var entryStream = index.Open())
using (var streamWriter = new StreamWriter(entryStream))
{
streamWriter.Write("hello");
}
}
var byteArray = memoryStream.ToArray();
return File(byteArray, "application/octet-stream", "media_files.zip");
}但是当客户试图打开拉链时
已发生异常。ArchiveException (FormatException:无法找到中央目录记录的结尾)
我对/mediafiles/{id}的这两行完全没有信心
var stream = await response.Content.ReadAsStreamAsync();
return File(stream, "application/octet-stream", "media_files.zip");也许问题就在这里。
我只需要转发file-service的响应,但我不知道为什么
发布于 2020-06-09 20:37:53
我认为您所遇到的问题是,在DownloadMediaFiles(int id)中,您使用的是在离开函数范围时处理的HttpClient。因此,在响应有效负载完成将其内容写入客户端之前,您从响应创建的stream也会被关闭和释放。因此,客户端接收到一个无法打开的不完整的zip文件。看这里供参考。
在这个答案中,您可以使用一个简单的解决方案,只需将响应流(来自$"http://file-service/bulk/{fileIds}"的响应流)读入字节数组,然后将其传递给客户端:
using var httpClient = new HttpClient();
var response = await httpClient.GetAsync($"http://file-service/bulk/{fileIds}");
var byteArr = await response.Content.ReadAsByteArrayAsync();
return File(byteArr, "application/octet-stream", "media_files.zip");您可能会意识到,这意味着将整个文件加载到内存中,如果您计划使用大型文件,甚至是中型文件(如果API应该由许多客户同时使用),则会很快成为一个问题。您的web应用程序很可能在某个时候耗尽内存。
相反,我看到了这篇文章,它展示了如何使用HttpClient从请求返回流的内容。您应该能够坚持这篇文章的第一部分(所有的ZIP文件和基于回调的响应都是无关的)。
回顾一下这篇文章,你所需要的就是这样的东西:
// Your ControllerClass.cs
private static HttpClient Client { get; } = new HttpClient();
[HttpGet("mediafiles/{id}")]
public async Task<IActionResult> DownloadMediaFiles(int id)
{
var fileIds = _myProvider.GetFileIdsForEntityId(id); // result be like "1,2,3,4"
var stream = await Client.GetStreamAsync($"http://file-service/bulk/{fileIds}");
return File(stream, "application/octet-stream", "media_files.zip");
}您会注意到,这里没有释放stream对象,而是将ASP.Net核心为你做这件事作为将响应有效负载写入客户端的一部分。存储在静态全局变量中的也没有被释放,这意味着您可以在请求之间重用它(通常建议不要每次需要时实例化新的HttpClient )。ASP.Net Core2.1及更高版本特别支持通过IHttpClientFactory接口为您注入客户端的依赖性。我建议你这样做,而不是静态变量。在这里读用于注入客户端工厂的最基本用法。
现在,您应该可以享受直接从您的“其他服务”流文件内容,而无需将其加载到您的API应用程序中的内存中。
https://stackoverflow.com/questions/62287083
复制相似问题