[C#][爬虫] 如何解码 Content-Disposition

最近在研究如何利用爬虫下载档案,有次在爬一个 Big5 编码的网站时,发生一件令我困惑的事。

该网站的回传 Header 大致如下,使用 Chrome 浏览器。

Cache-Control: privateContent-Disposition: attachment;filename=�������.csvContent-Length: 13Content-Type: application/octet-stream;charset=Big5Date: Sun, 12 Aug 2018 18:16:08 GMT

下载回来的档案名称是正确的 中文测试.csv,不过 Content-Disposition 看到的乱码 �������.csv 无法解回中文,因此我就很好奇浏览器是如何解码的。

接着想到可以用 Fiddler 拦截封包,看看有什么线索,Fiddler 的 Headers 视窗和浏览器看到的一样,不过 Hexview 就不同了,档名变成了 �������.csv

http://img2.58codes.com/2024/20106865sdCUtybLAZ.jpg

看到这个我又更困惑了,这应该是另一种编码,不过试了好久也是解不回来。

且这时发现 Requset 经过 Fiddler 后,从浏览器下载回来的档案名称变成了乱码 �������.csv,关掉 Fiddler 后又正常。

http://img2.58codes.com/2024/emoticon70.gif

到这里我已经彻底混乱了...,换用 HttpWebRequest 来看吧...,看到的是 ¤¤¤å´ú¸Õ.csv,我的老天鹅,试了三种方法,结果都不一样...

attachment;filename=¤¤¤å´ú¸Õ.csv

不过我感觉这个比较接近答案了,只要知道 Header 是用什么解码的,就能还原回去,Google 后发现原来 Http Header 内容必需以 ISO-8859-1 编码,马上试一下,果然被我猜对了,正确解回来了。

var fileName = "¤¤¤å´ú¸Õ.csv";//先以 ISO-8859-1 解码回 Big5 编码的位元组阵列var bytes = Encoding.GetEncoding("ISO-8859-1").GetByte(fileName);//再用 Big5 编码取出字串var str = Encoding.GetEncoding("Big5").GetString(bytes);//中文测试.csv

不过还是无法解释 Fiddler 看到的,由上面得出的结果,我猜测 HexView 看到的应该也是经过 ISO-8859-1 解码过的文字,那就将所有编码的结果列出来看看。

var fileName = "�������.csv";//先以 ISO-8859-1 编码var bytes = Encoding.GetEncoding("ISO-8859-1").GetByte(fileName);//测试所有编码var builder = new StringBuilder();foreach(var ei in Encoding.GetEncodings()){    builder.AppendFormat("xxx => {0} : {1} \r\n",         ei.Name, ei.GetEncoding().GetString(bytes));}//另存文字档File.WriteAllText($"{AppDomain.CurrentDomain.BaseDirectory}test.txt", builder.ToString());

在文字档内看到了熟悉的符号 �������.csv,看到这个符号我就知道原因了,Big5 编码的档案名称被 Fiddler 用 UTF-8 解码,然后又再用 UTF-8 编码一次,HexView 显示时再用 ISO-8859-1 解码,所以才会看到 �������.csv

http://img2.58codes.com/2024/20106865FU8Cx3iubz.jpg

证实一下。

var fileName = "中文测试.csv";//将档案名称以 Big5 编码var big5Bytes = Encoding.GetEncoding("Big5").GetBytes(fileName;//将 Big5 编码用 UTF-8 解码var utf8 = Encoding.GetEncoding("UTF-8").GetString(big5Bytes);//再以 UTF-8 编码一次var utf8Bytes = Encoding.GetEncoding("UTF-8").GetBytes(utf8);//以 ISO-8859-1 解码var str = Encoding.GetEncoding("ISO-8859-1").GetStrin(utf8Bytes);//�������.csv

这样就能解释为什么 Requset 经过 Fiddler 后,档案名称就会变成乱码,因为 Fiddler 用 UTF-8 错误解码,然后再编码一次传给 Chrome,不过这个动作只发生在 Header,Body 内容不受影响,我也不知道 Fiddler 为什么要这样做 ...

http://img2.58codes.com/2024/emoticon16.gif

接着我就有个疑问,浏览器如何判断 Content-Disposition 要用 Big5 解码呢,是依据 Content-Type 吗,因此我把 Content-Type 换成 UTF-8 看看。

Content-Type: application/octet-stream;charset=utf-8

结果还是正确的中文,所以和 Content-Type 无关,浏览器应该有其他的判断方式,现在各家浏览器在处理 Content-Disposition 都有各自的做法,并没有依照标準实作。

我测试了四种浏览器:

Chrome: 中文测试.csvFireFox: 中文测试.csvEdge: ¤¤¤å´ú¸Õ.csvIE11: 中文测试.csv

没想到 IE11 正确 Edge 竟然乱码,不过这也能说明只有 Edge 遵循标準。

Server 端程式

public class file : IHttpHandler{    public void ProcessRequest(HttpContext context)    {        context.Response.ContentEncoding = Encoding.GetEncoding("Big5");        context.Response.HeaderEncoding = Encoding.GetEncoding("Big5");        context.Response.ContentType =             "application/octet-stream;charset=Big5";        var data = "标题\n中文测试";        context.Response.Write(data);                context.Response.AddHeader("Content-Disposition",             "attachment;filename=中文测试.csv");    }        public bool IsReusable    {        get        {            return false;        }    }}

爬虫程式

var request = (HttpWebRequest)HttpWebRequest.Create(    "http://localhost:1651/file.ashx");//测试时设定 Proxy 到 Fiddler 上//request.Proxy = new WebProxy("127.0.0.1", 8888);var response = (HttpWebResponse)request.GetResponse();//取得 Content-Dispositionvar disposition = response.Headers["Content-Disposition"];var fileName = disposition.Split('=')[1];//将 filename 以 ISO-8859-1 编码var bytes = Encoding.GetEncoding("ISO-8859-1").GetBytes(fileName);//再以 Big5 解码var result = Encoding.GetEncoding("Big5").GetString(bytes);//另存档案using (var fs = new FileStream(    $"{AppDomain.CurrentDomain.BaseDirectory}{result}",    FileMode.Create, FileAccess.Write)){    using (var stream = response.GetResponseStream())    {        stream.CopyTo(fs);    }}

结语

最后觉得被 Fiddler 害得好惨,一开始测试 HttpWebRequest 时,因为有设 Proxy 到 Fiddler 上,所以看到的结果和浏览器一样,完全不知道 Fiddler 已经从中做了手脚,后来试到很晚,满脑子都是编码解码超级混乱,只好放弃去睡觉,睡醒后头脑比较清楚才找出原因,这篇虽然内容不多不过前后花了不少时间,感谢大家观看。
http://img2.58codes.com/2024/emoticon41.gif

参考文章

正确处理下载文档时HTTP头的编码问题(Content-Disposition)
下载文件设置header的filename要用ISO8859-1编码的原因
Fiddler2中文乱码问题


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章