关于下载⽂件是⽂件名的空格显⽰不出来和乱码问题转
在 asp 项⽬中,我们可以很⽅便地使⽤ Response.WriteFile() ⽅法向客户端输出⼀个⽂件。
实际使⽤ asp 向客户端输出⽂件流时,却出现了异常:
1、空格问题,当原⽂件的⽂件名中含有空格时,将引发客户端获取到的⽂件名与服务器端不⼀致。
2、中⽂字符乱码,准确的是⾮ ASCII 字符乱码,当原⽂件的⽂件名中含有⾮ ASCII 字符时,将引发客户端获取到的⽂件名错乱。
3、⼀些特殊字符不能被正常输出(当然这⾥我并不是那些不常见的符号)
注意,本⽂⽤ C# 代码解决了在⽬前四种流⾏浏览器中Asp 输出⽂件流时⽂件名的空格及中⽂字符乱码这两个问题。使⽤本⽂的代码,你将可以让 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("/", "();
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
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("/", "();
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>indexof空格算不算
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)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。