• 关于下载文件是文件名的空格显示不出来和乱码问题 转


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

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


    本文下面的内容将描述问题的具体表现,并对相关代码做一些解释;
    如果你不需要阅读这些内容,你可以直接下载示例代码。

    问题现象:

    对于第一个问题

    在IE中,当原文件名包含空格时,默认将被改成下划线,即“_”;如果我们在输出文件时对文件名使用 UrlEncode() 对其进行编码,空格将变成加号,即“+”。
    在 Opera 中,文件名不需要经过 UrlEncode() 即可正确地解析,但注意经过了 UrlEncode() 后也与IE一样,空格变成了加号。
    很 遗憾, Firefox 似乎并不欢迎含有空格的文件名,它会直接舍弃空格后面的部分。对于上图中的例子,没有进行 UrlEncode() 之前,Firefox 会得到一个“My.axd”的文件名,可以看到,它对文件类型把握并没有错误(只因为这由别外的部分负责);进行 UrlEncode() 之后,它的结果与 IE、Opera 等一致,空格变成了加号。

    对于第二个问题

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

    未进行 UrlEncode() 的中文文件名,IE 浏览器:

    已进行 UrlEncode() 的中文文件名,IE 浏览器:

    已进行 UrlEncode() 的中文文件名,Opera 浏览器

     至于 Firefox 与 Chrome 的图就不贴了,它们与 Opera 基本一致。

    问题的解决

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

    为了正确地编码,我参考一位外国人士的代码,使用并改进了16进制编码方法。参考下面的代码,可以大部分的解决问题。由于 Firefox 默认不支持中文,特别对 Firefox 用户做了一些处理,在下面的代码中能够体现。

    在输出文件地地方使用的代码:

    view plaincopy to clipboardprint?
    01.if (context != null)  
    02.{  
    03.    HttpRequest request = context.Request;  
    04.    HttpResponse response = context.Response;  
    05.    //本文件使用了 QueryString 来传递文件名,你也可以不使用  
    06.    if (!string.IsNullOrEmpty(context.Request.QueryString["file"]))  
    07.    {  
    08.        //取得客户端正在请求的文件的物理路径  
    09.        //不使用 QueryString 时,你可以使用 request.PhysicalPath 获取  
    10.        string path = context.Server.MapPath("~/") +  
    11.            context.Server.UrlDecode(context.Request.QueryString["file"]).Replace("/", "//").ToLower();  
    12.        if (File.Exists(path))  
    13.        {  
    14.            string extension = Path.GetExtension(path);  
    15.            response.ContentType = GetMimeType(extension);  
    16.            string fileName = System.IO.Path.GetFileName(path);  
    17.            if (request.UserAgent.ToLower().IndexOf("msie") > -1)  
    18.            {  
    19.                //当客户端使用IE时,对其进行编码;We should encode the filename when our visitors use IE  
    20.                //使用 ToHexString 代替传统的 UrlEncode();We use "ToHexString" replaced "context.Server.UrlEncode(fileName)"  
    21.                fileName = ToHexString(fileName);    
    22.            }  
    23.            if (request.UserAgent.ToLower().IndexOf("firefox") > -1)  
    24.            {  
    25.                //为了向客户端输出空格,需要在当客户端使用 Firefox 时特殊处理  
    26.               response.AddHeader("Content-Disposition", "attachment;filename=/"" + fileName + "/"");  
    27.            }  
    28.            else 
    29.                response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);  
    30.            response.WriteFile(path);  
    31.            response.End();  
    32.            return;  
    33.        }  
    34.    }  
    35.}  
    36.//正在请求的文件不存在;Cannot find the specified file  
    37.context.Response.Clear();  
    38.context.Response.Write("the data you are wanting to get does not exsit.");  
    39.context.Response.End(); 
            if (context != null)
            {
                HttpRequest request = context.Request;
                HttpResponse response = context.Response;
                //本文件使用了 QueryString 来传递文件名,你也可以不使用
                if (!string.IsNullOrEmpty(context.Request.QueryString["file"]))
                {
                    //取得客户端正在请求的文件的物理路径
                    //不使用 QueryString 时,你可以使用 request.PhysicalPath 获取
                    string path = context.Server.MapPath("~/") +
                        context.Server.UrlDecode(context.Request.QueryString["file"]).Replace("/", "//").ToLower();
                    if (File.Exists(path))
                    {
                        string extension = Path.GetExtension(path);
                        response.ContentType = GetMimeType(extension);
                        string fileName = System.IO.Path.GetFileName(path);
                        if (request.UserAgent.ToLower().IndexOf("msie") > -1)
                        {
                            //当客户端使用IE时,对其进行编码;We should encode the filename when our visitors use IE
                            //使用 ToHexString 代替传统的 UrlEncode();We use "ToHexString" replaced "context.Server.UrlEncode(fileName)"
                            fileName = ToHexString(fileName); 
                        }
                        if (request.UserAgent.ToLower().IndexOf("firefox") > -1)
                        {
                            //为了向客户端输出空格,需要在当客户端使用 Firefox 时特殊处理
                           response.AddHeader("Content-Disposition", "attachment;filename=/"" + fileName + "/"");
                        }
                        else
                            response.AddHeader("Content-Disposition", "attachment;filename=" + fileName);
                        response.WriteFile(path);
                        response.End();
                        return;
                    }
                }
            }
            //正在请求的文件不存在;Cannot find the specified file
            context.Response.Clear();
            context.Response.Write("the data you are wanting to get does not exsit.");
            context.Response.End();

     
    下面是核心处理,应该置于上述代码同一文件或可访问的其他类:


    view plaincopy to clipboardprint?
    01.#region 编码  
    02. 
    03./// <summary>  
    04./// 对字符串中的非 ASCII 字符进行编码  
    05./// </summary>  
    06./// <param name="s"></param>  
    07./// <returns></returns>  
    08.public static string ToHexString(string s)  
    09.{  
    10.    char[] chars = s.ToCharArray();  
    11.    StringBuilder builder = new StringBuilder();  
    12.    for (int index = 0; index < chars.Length; index++)  
    13.    {  
    14.        bool needToEncode = NeedToEncode(chars[index]);  
    15.        if (needToEncode)  
    16.        {  
    17.            string encodedString = ToHexString(chars[index]);  
    18.            builder.Append(encodedString);  
    19.        }  
    20.        else 
    21.        {  
    22.            builder.Append(chars[index]);  
    23.        }  
    24.    }  
    25. 
    26.    return builder.ToString();  
    27.}  
    28. 
    29./// <summary>  
    30./// 判断字符是否需要使用特殊的 ToHexString 的编码方式  
    31./// </summary>  
    32./// <param name="chr"></param>  
    33./// <returns></returns>  
    34.private static bool NeedToEncode(char chr)  
    35.{  
    36.    string reservedChars = "$-_.+!*'(),@=&";  
    37. 
    38.    if (chr > 127)  
    39.        return true;  
    40.    if (char.IsLetterOrDigit(chr) || reservedChars.IndexOf(chr) >= 0)  
    41.        return false;  
    42. 
    43.    return true;  
    44.}  
    45. 
    46./// <summary>  
    47./// 为非 ASCII 字符编码  
    48./// </summary>  
    49./// <param name="chr"></param>  
    50./// <returns></returns>  
    51.private static string ToHexString(char chr)  
    52.{  
    53.    UTF8Encoding utf8 = new UTF8Encoding();  
    54.    byte[] encodedBytes = utf8.GetBytes(chr.ToString());  
    55.    StringBuilder builder = new StringBuilder();  
    56.    for (int index = 0; index < encodedBytes.Length; index++)  
    57.    {  
    58.        builder.AppendFormat("%{0}", Convert.ToString(encodedBytes[index], 16));  
    59.    }  
    60. 
    61.    return builder.ToString();  
    62.} 
    63.
    64.
    65.#endregion  
    66. 
    67. 
    68./// <summary>  
    69./// 根据文件后缀来获取MIME类型字符串  
    70./// </summary>  
    71./// <param name="extension">文件后缀</param>  
    72./// <returns></returns>  
    73.static string GetMimeType(string extension)  
    74.{  
    75.    string mime = string.Empty;  
    76.    extension = extension.ToLower();  
    77.    switch (extension)  
    78.    {  
    79.        case ".avi": mime = "video/x-msvideo"; break;  
    80.        case ".bin":   
    81.        case ".exe":  
    82.        case ".msi":  
    83.        case ".dll":  
    84.        case ".class": mime = "application/octet-stream"; break;  
    85.        case ".csv": mime = "text/comma-separated-values"; break;  
    86.        case ".html":  
    87.        case ".htm":  
    88.        case ".shtml": mime = "text/html"; break;  
    89.        case ".css": mime = "text/css"; break;  
    90.        case ".js": mime = "text/javascript"; break;  
    91.        case ".doc":  
    92.        case ".dot":  
    93.        case ".docx": mime = "application/msword"; break;  
    94.        case ".xla":  
    95.        case ".xls":   
    96.        case ".xlsx": mime = "application/msexcel"; break;  
    97.        case ".ppt":   
    98.        case ".pptx": mime = "application/mspowerpoint"; break;              
    99.        case ".gz": mime = "application/gzip"; break;  
    100.        case ".gif": mime = "image/gif"; break;  
    101.        case ".bmp": mime = "image/bmp"; break;  
    102.        case ".jpeg":   
    103.        case ".jpg":   
    104.        case ".jpe":   
    105.        case ".png": mime = "image/jpeg"; break;  
    106.        case ".mpeg":   
    107.        case ".mpg":  
    108.        case ".mpe":   
    109.        case ".wmv": mime = "video/mpeg"; break;  
    110.        case ".mp3":   
    111.        case ".wma": mime = "audio/mpeg"; break;  
    112.        case ".pdf": mime = "application/pdf"; break;  
    113.        case ".rar": mime = "application/octet-stream"; break;  
    114.        case ".txt": mime = "text/plain"; break;  
    115.        case ".7z":  
    116.        case ".z": mime = "application/x-compress"; break;  
    117.        case ".zip": mime = "application/x-zip-compressed"; break;  
    118.        default:  
    119.            mime = "application/octet-stream";  
    120.            break;  
    121.    }  
    122.    return mime;  
    123.} 
        #region 编码

        /// <summary>
        /// 对字符串中的非 ASCII 字符进行编码
        /// </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>
        /// 判断字符是否需要使用特殊的 ToHexString 的编码方式
        /// </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>
        /// 为非 ASCII 字符编码
        /// </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();
        }


        #endregion


        /// <summary>
        /// 根据文件后缀来获取MIME类型字符串
        /// </summary>
        /// <param name="extension">文件后缀</param>
        /// <returns></returns>
        static string GetMimeType(string extension)
        {
            string mime = string.Empty;
            extension = extension.ToLower();
            switch (extension)
            {
                case ".avi": mime = "video/x-msvideo"; break;
                case ".bin":
                case ".exe":
                case ".msi":
                case ".dll":
                case ".class": mime = "application/octet-stream"; break;
                case ".csv": mime = "text/comma-separated-values"; break;
                case ".html":
                case ".htm":
                case ".shtml": mime = "text/html"; break;
                case ".css": mime = "text/css"; break;
                case ".js": mime = "text/javascript"; break;
                case ".doc":
                case ".dot":
                case ".docx": mime = "application/msword"; break;
                case ".xla":
                case ".xls":
                case ".xlsx": mime = "application/msexcel"; break;
                case ".ppt":
                case ".pptx": mime = "application/mspowerpoint"; break;           
                case ".gz": mime = "application/gzip"; break;
                case ".gif": mime = "image/gif"; break;
                case ".bmp": mime = "image/bmp"; break;
                case ".jpeg":
                case ".jpg":
                case ".jpe":
                case ".png": mime = "image/jpeg"; break;
                case ".mpeg":
                case ".mpg":
                case ".mpe":
                case ".wmv": mime = "video/mpeg"; break;
                case ".mp3":
                case ".wma": mime = "audio/mpeg"; break;
                case ".pdf": mime = "application/pdf"; break;
                case ".rar": mime = "application/octet-stream"; break;
                case ".txt": mime = "text/plain"; break;
                case ".7z":
                case ".z": mime = "application/x-compress"; break;
                case ".zip": mime = "application/x-zip-compressed"; break;
                default:
                    mime = "application/octet-stream";
                    break;
            }
            return mime;
        }

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

    如果复制代码后运行不正常,可以参考在示例代码文件的处理情况,在这里下载示例代码文件,示例文件是一个 HttpHandler,因此你可能需要为它在 Web.Config 中做相关配置,关于配置方法,请参考其他资料。你可以按你的需要来修改示例代码

    转自:http://blog.csdn.net/ciznx/archive/2010/05/26/5625222.aspx

  • 相关阅读:
    TortoiseSVN 实现文件的移动
    CSS选择器
    聚合报告90%参数说明
    JMeter响应断言详解
    测试结果分析
    Selenium+TestNG+Java报错:org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:40)
    在IntelliJ IDEA通过Maven添加依赖无效
    CentOS下安装TestLink
    Mysql存储过程生成指定格式的订单号
    Mysql使用函数生成指定随机字段内容
  • 原文地址:https://www.cnblogs.com/gester/p/4588897.html
Copyright © 2020-2023  润新知