彻底解决Asp.net 文件下载时文件名的中文乱码和空格异常全球性技术难题

来源:互联网 发布:网络媒介 编辑:程序博客网 时间:2024/05/09 15:25

在 asp.net 项目中,我们可以很方便地使用 Response.WriteFile() 方法向客户端输出一个文件。
实际使用 asp.net 向客户端输出文件流时,却出现了异常:
1、空格问题,当原文件的文件名中含有空格时,将引发客户端获取到的文件名与服务器端不一致。Spaces cannot be supported by some browsers
2、中文字符乱码,准确的是非 ASCII 字符乱码,当原文件的文件名中含有非 ASCII 字符时,将引发客户端获取到的文件名错乱。Non-US-ASCII characters cause incorrect result
3、一些特殊字符不能被正常输出(当然这里我并不是那些不常见的符号)special symbols cannot be output correctly

注意,本文用 C# 代码解决了在目前四种流行浏览器中Asp.net 输出文件流时文件名的空格及中文字符乱码这两个问题。使用本文的代码,你将可以让 IE(Internet Explorer)、Opera、Firefox 及 Chrome 的用户享受到没有乱码且支持空格文件名的文件输出引擎,同时支持文件名中各种像“# $ % ^ &”等常见的符号,如 "Microsoft.Asp.Net.doc" 、“F ile;;!@%#^&y.doc” 这样的文件名也可以了。请看下图:

Two Problems occur when we output a file via stream in asp.net: Filename with special symbols(e.g: space; # @ ! $ ) or Non-US-ASCII characters either cannot be supported by some browsers or cause incorrect filename in client machine. After reading this article, you can provide perfect filename in the all four popular browsers (IE,that is Internet Explorer; Opera; Firefox and Chrome), even your filename contains Non-US-ASCII characters or & and Spaces; something else can excite you that you can even put any number of special symbols in your filename if you like from now on (e.g: the filename "Microsoft.Asp.Net.doc"  and "F ile;;!@%#^&y.doc" can both be output correctly ).  See the following figure:



本文下面的内容将描述问题的具体表现,并对相关代码做一些解释;
如果你不需要阅读这些内容,你可以直接下载示例代码:SpecifiedFileHandler.rar (2.22 kb) (Click the link to download the sample source code (wirtten with C#))
本文提供了一个 HttpHandler 以处理文件下载请求,你可以把它按你的需要做修改;
I provided a HttpHandler to process requests for files in this article, you can modify this file for your certain use.


问题现象:

对于第一个问题

当原文件名包含空格时,默认将被改成下划线,即“_”;如果我们在输出文件时对文件名使用 UrlEncode() 对其进行编码,空格将变成加号,即“+”。
看下面几个图,分别是没有使用 UrlEncode() 编码文件名和使用了UrlEncode()  的时候,全英文的原文件名的文件输出到客户端的情况:

在 Opera 中表现得比较一致:英文的文件名不需要经过 UrlEncode() 即可正确地解析,但注意经过了 UrlEncode() 后也与IE一样,空格变成了加号。
很遗憾, Firefox 似乎并不欢迎含有空格的文件名,它会直接舍弃空格后面的部分。对于上图中的例子,没有进行 UrlEncode() 之前,Firefox 会得到一个“My.axd”的文件名,可以看到,它对文件类型把握并没有错误(只因为这由别外的部分负责);进行 UrlEncode() 之后,它的结果与 IE、Opera 等一致,空格变成了加号。


对于第二个问题

第二个问题有点复杂了。
当原文件名包含中文或其他非英文字符时,由于编码的错误,默认情况很糟糕,竟然完全是无法辨识的乱码;如果我们在输出文件时对文件名进行 UrlEncode() 对其进行编码,这些中文将能正确地被显示;
但注意,问题并没有完。在Opera 或 Firefox 中,不需要经过 UrlEncode() 即能正确地显示了;不幸地是,如果经过了 UrlEncode(),它们将无法正确地解析。
看下面几个图,分别是没有使用 UrlEncode() 编码文件名和使用了 UrlEncode() 的时候,全英文的原文件名的文件输出到客户端的情况:




问题的解决

我们可以总结如下规律:
 Internet Explorer 能在客户端已经UrlEncode() 的字符,包括空格在内;而 Opera 等其他浏览器可以解析未经 UrlEncode() 的直接输出的字符(这意味着,对于使用Opera或其他客户端的客户,我们不应该对它进行 UrlEncode()编码)

为了正确地编码,我参考一位外国人士的代码,使用了并改进了16进制编码方法。
参考下面的代码,可以大部分的解决问题。由于 Firefox 不支持空格,所以以下代码对于 Firefox ,仍不能完成具有空格的文件名的正确输出:

在输出文件地地方使用的代码:
                string path = request.PhysicalPath;//获取客户端请求的文件的物理地址
                if (File.Exists(path))
                  {
                        string fileName = System.IO.Path.GetFileName(path);   // 获取文件的名字
                        if (Request.UserAgent.Contains("MSIE") || Request.UserAgent.Contains("msie")) 
                           {         
                            // 如果客户端使用 Microsoft Internet Explorer,则需要编码
                            fileName = ToHexString(fileName);     // 如果使用  fileName =Server.UrlEncode(fileName);  则会出现上文中出现的情况
                           }
                        Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
                        Response.WriteFile(path);
                        Response.End();
                 }
 
应该置于上述代码同一文件或可访问的其他类的几个函数:


        /// <summary>
        /// 为字符串中的非英文字符编码
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static string ToHexString(string s)
        {
            char[] chars = s.ToCharArray();
            StringBuilder builder = new StringBuilder();
            for (int index = 0; index < chars.Length; index++)
            {
                bool needToEncode = NeedToEncode(chars[index]);
                if (needToEncode)
                {
                    string encodedString = ToHexString(chars[index]);
                    builder.Append(encodedString);
                }
                else
                {
                    builder.Append(chars[index]);
                }
            }

            return builder.ToString();
        }

        /// <summary>
        ///指定 一个字符是否应该被编码
        /// </summary>
        /// <param name="chr"></param>
        /// <returns></returns>
        private static bool NeedToEncode(char chr)
        {
            string reservedChars = "$-_.+!*'(),@=&";

            if (chr > 127)
                return true;
            if (char.IsLetterOrDigit(chr) || reservedChars.IndexOf(chr) >= 0)
                return false;

            return true;
        }

        /// <summary>
        /// 为非英文字符串编码
        /// </summary>
        /// <param name="chr"></param>
        /// <returns></returns>
        private static string ToHexString(char chr)
        {
            UTF8Encoding utf8 = new UTF8Encoding();
            byte[] encodedBytes = utf8.GetBytes(chr.ToString());
            StringBuilder builder = new StringBuilder();
            for (int index = 0; index < encodedBytes.Length; index++)
            {
                builder.AppendFormat("%{0}", Convert.ToString(encodedBytes[index], 16));
            }
           return builder.ToString();
        }

 此外,针对一些浏览器做了一些特殊的处理,已经体现在本文示例代码的注释中。此代码已经能非常完好地解决问题了,在 Internet Explorer 、Opera、Firefox 及 Chrome 中得到的体验一致,支持中文,支持空格的正常输出。你可以按你的需要来修改示例代码。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 电脑自带鼠标动不了怎么办 笔记本电脑自带鼠标动不了怎么办 包包弹簧扣松了怎么办 耳钉氧化成黑色怎么办 想买点小股票玩玩要怎么办 玩具子弹打到眼睛怎么办 玩具汽车遥控器码乱了怎么办 索尼A7屏幕花了怎么办? 汽车冷冻器坏了怎么办 sim卡损坏怎么办 补卡 异或门一个输入怎么办 迷你世界加不了好友怎么办 电脑软件显示无效应用程序怎么办 美的滚筒洗衣机打不开门怎么办 手机存储卡坏了怎么办 回南天地板潮湿怎么办 lg滚筒洗衣机门打不开怎么办 西门子滚桶洗衣机门打不开怎么办 洗衣机离合器螺丝卸不动怎么办 门锁保险栓坏了怎么办 小车电瓶没电了怎么办 重装机兵战车底盘坏了怎么办 父亲沉迷安利十年该怎么办 脚的大脚骨痛怎么办 自考准考证号忘记了怎么办 有桌子老师不出马怎么办 电商遇到职业打假人怎么办 超市遇到职业打假人怎么办 阿里巴巴碰到职业打假人怎么办 商家遇到职业打假人怎么办 买过期食品不赔怎么办 淘宝卖假货遇到打假师怎么办 网店遇到职业打假人怎么办 职业打假师把我起诉法院怎么办 被职业打假举报了怎么办 车档杆拉不动显示不在p档怎么办 宜人贷还不起了怎么办 买高跟鞋一只脚合适一只脚挤怎么办 脚瘦穿高跟鞋撑不起来怎么办 银川市阅海幼儿园进不去怎么办 考编专业不对口怎么办