首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在c# api中转发流

在c# api中转发流
EN

Stack Overflow用户
提问于 2020-06-09 16:14:12
回答 1查看 1.3K关注 0票数 0

在我的服务中,我们需要获得另一个服务创建的zip文件并返回它。

这是我的代码(这个问题的代码已经简化了):

代码语言:javascript
复制
[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 (针对这个问题简化了代码):

代码语言:javascript
复制
[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}的这两行完全没有信心

代码语言:javascript
复制
var stream = await response.Content.ReadAsStreamAsync();

return File(stream, "application/octet-stream", "media_files.zip");

也许问题就在这里。

我只需要转发file-service的响应,但我不知道为什么

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-06-09 20:37:53

我认为您所遇到的问题是,在DownloadMediaFiles(int id)中,您使用的是在离开函数范围时处理的HttpClient。因此,在响应有效负载完成将其内容写入客户端之前,您从响应创建的stream也会被关闭和释放。因此,客户端接收到一个无法打开的不完整的zip文件。看这里供参考。

这个答案中,您可以使用一个简单的解决方案,只需将响应流(来自$"http://file-service/bulk/{fileIds}"的响应流)读入字节数组,然后将其传递给客户端:

代码语言:javascript
复制
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文件和基于回调的响应都是无关的)。

回顾一下这篇文章,你所需要的就是这样的东西:

代码语言:javascript
复制
// 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应用程序中的内存中。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62287083

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档