我正在生成一些JSON内容,然后从MVC控制器操作将压缩后的内容返回给用户之前,对该内容进行GZipping。
由于可以将生成的文件输出到磁盘,然后可以使用GZip打开该文件,因此内容的生成和gzipping工作正常。但是,将内容返回到浏览器时,内容已损坏。
我尝试了几种将内容返回到浏览器的方法,例如
return File(byte[], "application/gzip");
return new FileStreamResult(stream, "application/gzip")
还可以使用BinaryWrite()和WriteFile()方法直接写入Response
无论我做什么,在浏览器中收到的文件都已损坏。
此代码显示了我当前尝试返回文件内容的方式。
// 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读取,但是浏览器收到的文件已损坏,因此我确信我的文件创建正常。但是以某种方式将文件写入Response后,它已损坏。
我确实想知道是否某种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个字节。我无法弄清楚为什么Response以这种方式处理此加密数据。我的假设是,当Byte []数组只是纯文本作为byte []数组时,则可以很好地处理。一旦它是一个适当的byte []数组,即不仅仅是一个字符串作为byte []数组,那么响应中还将进行其他一些编码转换。
答案 0 :(得分:0)
您可以尝试ActionFilterAttribute
基本上,响应过滤器会在编写响应输出流时对其进行查看,并转换流经它的数据。
GZip/Deflate Compression in ASP.NET MVC
public class CompressAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
if (string.IsNullOrEmpty(encodingsAccepted)) return;
encodingsAccepted = encodingsAccepted.ToLowerInvariant();
var response = filterContext.HttpContext.Response;
if (encodingsAccepted.Contains("deflate"))
{
response.AppendHeader("Content-encoding", "deflate");
response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
}
else if (encodingsAccepted.Contains("gzip"))
{
response.AppendHeader("Content-encoding", "gzip");
response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
}
}
}
[Compress]
[HttpGet]
public ActionResult GetFile()
{...}