【发布时间】:2019-05-17 16:45:35
【问题描述】:
我正在生成一些 JSON 内容,然后在将压缩后的内容返回给用户之前,从 MVC 控制器操作中对该内容进行 GZipping。
内容的生成和 gzipping 工作正常,因为我可以将生成的文件输出到磁盘,然后我可以使用 GZip 打开该文件。但是,当内容返回浏览器时,内容已经损坏。
我尝试了几种不同的方法将内容返回到浏览器,例如
return File(byte[], "application/gzip");
return new FileStreamResult(stream, "application/gzip")
还可以使用 BinaryWrite() 和 WriteFile() 方法直接写入响应
无论我做什么,我在浏览器中收到的文件都是损坏的。
此代码显示了我当前尝试返回文件内容的方式。
// This line writes my content byte[] array to disk. This file when opened with gzip works fine.
System.IO.File.WriteAllBytes(@"C:\temp\test.vcp", result.FileBytes);
// Writing out the byte array to the Response results in a corrupt file. I have also attempted to Response.WriteFile(@"C:\temp\test.vcp") which also results in a corrupt file.
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", result.FileBytes.Length.ToString());
Response.BinaryWrite(result.FileBytes);
Response.Flush();
Response.Close();
Response.End();
由于我正在创建的文件可以写入磁盘,并且可以使用 Gzip 读取,但是浏览器接收到的文件已损坏,因此我确信我的文件创建正常。但不知何故,在将文件写入响应后,它被损坏了。
我确实想知道是否某种 HTTPHandler 正在操纵结果,但我没有添加任何处理程序(我可以看到)。
我目前通过 IISExpress 在本地运行应用程序。如何检查哪些 HttpHandlers/HttpModules 应用于管道?
最终我希望在浏览器中收到与写入磁盘完全相同的文件。
作为参考,我生成的内容长度为 132 字节,但浏览器接收到 216 字节。我注意到在查看接收数据的字节结构时,内容中有 3 个字节的重复模式,其值为 239、191、189。看起来结果字节数组已被这些填充或填充3 个字节。
编辑
这是一个独立的 Action 方法来演示这个问题。
[HttpGet]
public void GetFile()
{
byte[] text = Encoding.ASCII.GetBytes(@"{""PetName"":""Doggy McDocFace"",""OwnerName"":""Kurt""}");
byte[] compressed = Compress(text);
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = "ExampleFile.vcp",
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = true,
};
System.IO.File.WriteAllBytes(@"C:\temp\ExampleFile.vcp", compressed);
Response.Clear();
Response.ContentType = "application/gzip";
Response.AppendHeader("Content-Disposition", cd.ToString());
Response.AddHeader("Content-Length", compressed.Length.ToString());
Response.BinaryWrite(compressed);
Response.Flush();
Response.Close();
Response.End();
}
public byte[] Compress(byte[] raw)
{
using (var memory = new MemoryStream())
{
using (var gzip = new GZipStream(memory, CompressionMode.Compress, true))
{
gzip.Write(raw, 0, raw.Length);
}
return memory.ToArray();
}
}
我在这里欺骗了我的 JSON 内容,然后对其进行压缩。写入磁盘的文件工作正常,可以用我的 GZip 应用程序打开(我使用 7-zip)。但是,浏览器收到的文件已损坏。 7-zip 无法将其识别为 gzip 文件。
编辑 2
所以看起来(感谢@Will)写入 Response 的内容与 UTF-8 编码不符。我无法弄清楚如何,就像上面的示例一样,我正在使用 Encoding.ASCII.GetBytes() 将我的字符串转换为 byte[] 数组。
我试过设置
Response.Charset = Encoding.ASCII.EncodingName;
Response.ContentEncoding = Encoding.ASCII;
但这仍然不会导致下载的文件有效。
编辑 3
我已将问题范围缩小到数据的 GZip 加密。如果我不加密数据,那么纯文本文件可以正常下载。但是,加密 byte[] 数组然后将该 byte[] 数组写入 Repsonse 会导致看起来像 UTF-8 编码的问题。任何值超过 127 的字节都会被我进一步提到的 3 个字节损坏。我无法弄清楚为什么响应以这种方式处理这些加密数据。我的假设是,当 Byte[] 数组只是作为 byte[] 数组的纯文本时,则处理得很好。只要它是一个正确的 byte[] 数组,即不仅仅是一个作为 byte[] 数组的字符串,那么响应中就会进行一些其他的编码转换。
【问题讨论】:
-
您需要向我们展示您的实际代码,这里还不够。
-
以上是我的实际代码,与我将文件写入响应的位置有关。我无法发布我的整个代码,因为它由我的解决方案中多个项目的多个接口实现组成。我在上面的代码 sn-p 中说的是我有一个字节 []。我可以将该字节 [] 写入磁盘,它是 132 字节。当我将该 byte[] 返回给浏览器时,接收到的数据是 216 个字节,并且生成的文件无法通过 gzip 打开。因此,在写入响应后,内容已损坏。
-
Hmmmmmmmmmmmmmmmmmmmmmmmmmmmmm stackoverflow.com/questions/50692651/… "结果变成byte[381],里面是[239, 191, 189]的循环:"
-
oooooooooooooooooooooooooooooh haacked.com/archive/2012/01/30/…
-
@Will 谢谢,这很有趣。在我上面的(编辑)示例中,我使用 Encoding.ASCII.GetBytes() 将我的字符串转换为字节。保存到磁盘时仍然会产生有效文件,但通过响应返回到浏览器时无效。我承认我在正确的(未简化的)代码中使用了 Encoding.UTF8.GetBytes。
标签: c# asp.net-mvc