Doc*_*Doc 0 c# api zip stream asp.net-core
在我的服务中,我们需要获取另一个服务创建的 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");
}
Run Code Online (Sandbox Code Playgroud)
使用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");
}
Run Code Online (Sandbox Code Playgroud)
但是当客户试图打开 zip 我们得到
发生异常。ArchiveException (FormatException: 找不到中央目录记录的结尾)
我对这两行完全没有信心 /mediafiles/{id}
var stream = await response.Content.ReadAsStreamAsync();
return File(stream, "application/octet-stream", "media_files.zip");
Run Code Online (Sandbox Code Playgroud)
可能问题就在那里。
我只需要转发file-service回复,但我不知道为什么
我相信您遇到的问题是,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");
Run Code Online (Sandbox Code Playgroud)
您可能会意识到这意味着将整个文件加载到内存中,如果您计划处理大文件,或者如果 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");
}
Run Code Online (Sandbox Code Playgroud)
您会注意到,该stream对象并未在此处处理,但ASP.Net Core 会为您执行此操作,作为将响应负载写入客户端的一部分。Client存储在静态全局变量中的which 也不会被处理,这意味着您可以在请求之间重用它(通常建议不要在HttpClient每次需要时实例化一个新的)。ASP.Net Core 2.1 及更高版本特别支持通过IHttpClientFactory接口为您注入客户端的依赖项。我建议你这样做而不是静态变量。阅读此处了解注入客户端工厂的最基本用法。
现在,您应该能够直接从“其他服务”流式传输文件内容,而无需将其加载到 API Web 应用程序的内存中。
| 归档时间: |
|
| 查看次数: |
498 次 |
| 最近记录: |