在 Web API 响应中添加 zip 文件作为内容,下载时文件大小加倍

Posted

技术标签:

【中文标题】在 Web API 响应中添加 zip 文件作为内容,下载时文件大小加倍【英文标题】:Adding zip file as Content in Web API response doubling file size on download 【发布时间】:2020-08-03 12:30:01 【问题描述】:

我正在将 zip 文件保存到 AWS S3 存储桶。我现在正在尝试创建一个 C# .NET API,它允许我从存储桶中下载指定的密钥并将其保存到 Content 密钥中的 HttpResponseMessage。

我参考了以下问题来设置我对 zip 文件的回复:How to send a zip file from Web API 2 HttpGet

我已修改上一个问题中的代码,使其改为从 TransferUtility 流中读取。

问题是我在尝试提取或查看如下所示的文件时遇到错误:

我从 API 返回的响应如下所示:

相关代码如下:

[HttpGet, Route("GetFileFromS3Bucket")]
public HttpResponseMessage GetFileFromS3Bucket(string keyName)

    HttpResponseMessage response = new HttpResponseMessage();
    string bucketName = "myBucket";
    RegionEndpoint bucketRegion = RegionEndpoint.ARegion;
    IAmazonS3 s3Client;
    s3Client = new AmazonS3Client(bucketRegion);

    try
    
        var fileTransferUtility = new TransferUtility(s3Client);
        var stream = fileTransferUtility.OpenStream(bucketName, keyName);
        response.Content = new StreamContent(stream);
        response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
        response.Content.Headers.ContentDisposition.FileName = keyName + ".zip";
        response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
        response.StatusCode = HttpStatusCode.OK;
    
    catch (Exception e)
    
        response.Content = new StringContent("Something went wrong, error: " + e.Message);
        response.StatusCode = HttpStatusCode.InternalServerError;
    

    return response;

故障排除结果:

来自 Web API 的文件的大小几乎是 S3 中预期大小的两倍。这在不同文件中是一致的 将存储桶更改为可公开访问没有帮助(设置后恢复为不允许公开访问) 将文件类型更改为 XML 未显示格式正确的错误(有一个建议,如果 S3 提供错误,您可能会收到 XML 响应) 将 S3 流保存到文件,然后直接保存到文件会产生正确的文件大小。似乎可以肯定地说来自 S3 的流不是问题

HTTPResponseMessage 处理 zip 文件的方式似乎存在问题。我不确定它实际上是在服务器端,还是在客户端来解析数据,而 Swagger 根本无法做到这一点。任何帮助将不胜感激。

更新 1 我不相信这个字符串是 Base64 编码的,因为我将流转换为字符串得到的结果如下:

我已经更新了代码示例,其中两行显示了从流到字符串的转换。

更新 2 我已经确认问题在于响应如何处理流,或者响应本身的某些内容。从 S3 下载文件流并保存到本地计算机上的新文件会生成按预期打开的有效文件。

更新 3 链接到带有测试文件的 GDrive 文件夹:https://drive.google.com/drive/folders/1q_N3NTHz5E_nebtBQJHor3HfqUZWhGgd?usp=sharing 很遗憾,我无法提供对原始文件的访问权限,因为它包含敏感数据。但是,提供的文件仍然会导致相同的问题。 有趣的是,测试文件看起来像:

文件名两边的下划线很奇怪。

我正在运行以下相关软件包:

更新 4 我在各种文件中发现了以下 UTF8 引用:

文件:configuration91.svcinfo

我在项目的任何地方都找不到任何关于“responseEncoding”的内容。

【问题讨论】:

看起来 fie 是 GZIP 或 Base64 字符串。两者都会比原始文件大,因为二进制文件被打包成可读的 ASCII 字符。 @jdweng 这会导致文件损坏吗?如果是这样,我如何确保来自 S3(或来自 Web API,我不确定它会在哪里中断)的输出是我希望在我的响应内容中提供的 .zip 格式? 如果你得到一个 Base64 字符串,那么你需要使用 byte[] data = Convert.FromBase64String(string) 然后将字节作为二进制文件保存到文件中。 @jdweng 为回复干杯,我做了一些测试并确认返回的数据既不是 GZIP 也不是 Base64 字符串。我有点担心 AWS 库处理数据的方式可能存在问题。 如果你做一个小压缩包——比如 5KB 压缩包,它的大小还会翻倍吗?如果你能做到这一点,然后将原始 zip 文件与“大小翻倍”的文件一起发布,我很确定我可以告诉你出了什么问题。 【参考方案1】:

我要抛出一个答案,因为发生在你身上的事情是非正统的。我在很多事情上都使用 S3,并且在过去没有任何问题地完成了你正在做的事情。为了确保我模仿你在做什么,我复制了你的代码:

[HttpGet, Route("GetFileFromS3Bucket/keyName")]
public HttpResponseMessage GetFileFromS3Bucket(string keyName)

    string bucketName = "testzipfilesagain";
    string awsAccessKey = "AKIAJ********A3QHOUA";
    string awsSecretKey = "IYUJ9Gy2wFCQ************dCq5suFS";

    IAmazonS3 client = new AmazonS3Client(awsAccessKey, awsSecretKey, RegionEndpoint.USEast1);

    var fileTransferUtility = new TransferUtility(client);
    var stream = fileTransferUtility.OpenStream(bucketName, "md5.zip");

    var resp = new HttpResponseMessage();

    resp.Content = new StreamContent(stream);
    resp.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
    resp.Content.Headers.ContentDisposition.FileName = keyName + ".zip";
    resp.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/zip");
    resp.StatusCode = HttpStatusCode.OK;

    return resp;

这些是我安装的包:

  <ItemGroup>
    <PackageReference Include="AWSSDK.S3" Version="3.3.111.37" />
    <PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.2.0" />
    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
  </ItemGroup>

一切运行良好。

尝试对您的代码进行故障排除将徒劳无功,因为它工作得非常好,但是您的环境有问题。

因此,这不是对您的问题的回答,而是对如何尝试解决手头的问题并克服这个问题的回答。

    确保您的 nuget 包是最新的 您的管道中是否注入了任何中间件?如果有,是什么? 发布您的 startup.cs -- 可能您的 Configure 例程中出现问题。 您能开始一个全新的项目并在其中尝试您的代码吗? 您能否尝试一个 5KB 的小 zip 文件并发布原始文件和损坏的文件以便我们查看?

我很想深入了解这一点,因为我真的很想解决这些类型的问题。


编辑 1

所以我查看了 zip 文件,它们已通过 UTF8 编码过程运行。因此,如果您使用原始 zip 文件并在其上运行此代码:

    var goodBytes = File.ReadAllBytes("Some test to upload to S3.zip");
    var badBytes = File.ReadAllBytes("_Some test to upload to S3.zip.zip_");

    File.WriteAllText("Some test to upload to S3.zip.utf8", Encoding.UTF8.GetString(goodBytes));
    var utf8EncodedGoodBytes = File.ReadAllBytes("Some test to upload to S3.zip.utf8");

    var identical = badBytes.SequenceEqual(utf8EncodedGoodBytes);

结果是:

我将进行一些研究并找出可能导致您的流变成 UTF-8 编码的原因。你的配置中有looks like this 的东西吗?您能否在整个解决方案中搜索类似于“utf”、“utf8”或“utf-8”的任何内容?

【讨论】:

嗨,安迪,感谢您的回答,我现在将解决所有这些问题并让您知道结果。非常感谢,我很高兴有人喜欢配置的东西,我讨厌它,因为我很容易找到新的方法来搞砸它 好的,解决这些问题: 1. Nugets 已经过时了,我来擦洗一下。但是这并没有解决问题 2. 没有中间件 3. 我实际上没有 startup.cs,因为我没有运行 .NET Core(如果我完全误解了这一点,请纠正我) 4. 我会让你知道不久的结果 5. Link in Question 这看起来很有希望!出于兴趣,您是如何发现它们是 UTF8 编码的?只是想知道我自己将来的故障排除。此外,文件最初的编码是什么?我将看看是否有一种方法可以强制给定 HttpResponseMessage 的内容的编码类型 @Stevo -- 我在十六进制编辑器中打开它,并在文件中看到字节 0xef 0xbf 0xbd 序列,这是 UTF-8 令牌。不管怎样,你真的应该在你的解决方案中搜索字符串“utf-8”,看看配置中是否有任何内容。 抱歉,我更新了我的问题并忘记提及它。请参阅更新 4 中的图片和注释

以上是关于在 Web API 响应中添加 zip 文件作为内容,下载时文件大小加倍的主要内容,如果未能解决你的问题,请参考以下文章

React - 从字节字符串中的 API 响应正文下载 Zip 文件?

如何测试使用邮递员提供 .zip 文件的 REST API?

下载Ajax响应作为zip文件?

在 asp.net web api 中使用 MemoryStream 和 ZipArchive 将 zip 文件返回给客户端

在Web演示中添加图像作为响应(DialogFlow)

如何使用 Web API 作为特定用户将文件添加到 sharepoint 2016