Skip to content

Instantly share code, notes, and snippets.

@jzwang-dev
Created January 21, 2021 15:05
Show Gist options
  • Save jzwang-dev/886d631f13ed050fd629f6fb53abc952 to your computer and use it in GitHub Desktop.
Save jzwang-dev/886d631f13ed050fd629f6fb53abc952 to your computer and use it in GitHub Desktop.
ASP.NET下載檔案包含中文檔名Header處理
// 下載檔案包含中文檔名Header處理
// Wang, Jian-Zhong
using System.Text;
namespace JZLib
{
public static class HeaderUtil
{
private const string HexDigits = "0123456789ABCDEF";
private static void AddByteToStringBuilder(byte b, StringBuilder builder)
{
builder.Append('%');
int i = b;
AddHexDigitToStringBuilder(i >> 4, builder);
AddHexDigitToStringBuilder(i % 16, builder);
}
private static void AddHexDigitToStringBuilder(int digit, StringBuilder builder)
{
builder.Append(HexDigits[digit]);
}
private static string CreateRfc2231FileName(string filename)
{
StringBuilder builder = new StringBuilder("");
byte[] filenameBytes = Encoding.UTF8.GetBytes(filename);
foreach (byte b in filenameBytes)
{
if (IsByteValidHeaderValueCharacter(b))
{
builder.Append((char)b);
}
else
{
AddByteToStringBuilder(b, builder);
}
}
return builder.ToString();
}
/// <summary>
/// 取得下載檔案名稱含Unicode字元如中文的Content-Disposition Header值
/// </summary>
/// <param name="filename">含Unicdoe字元的檔案名稱</param>
/// <param name="inline">true表示以inline的形式呈現於頁面中以瀏覽器預覽,false表示在Header中指定attachment強制瀏覽器下載</param>
/// <returns>下載檔案名稱含Unicode字元如中文的Content-Disposition Header值</returns>
/// <example>
/// Response.AppendHeader("Content-Disposition", JZLib.HeaderUtil.GetUnicodeContentDisposition("測試中文.txt", false));
/// </example>
public static string GetUnicodeContentDisposition(string filename, bool inline = true)
{
return $"{(inline ? "inline" : "attachment")}; filename*=UTF-8''{CreateRfc2231FileName(filename)}";
}
// Application of RFC 2231 Encoding to Hypertext Transfer Protocol (HTTP) Header Fields, sec. 3.2
// http://greenbytes.de/tech/webdav/draft-reschke-rfc2231-in-http-latest.html
private static bool IsByteValidHeaderValueCharacter(byte b)
{
if ((byte)'0' <= b && b <= (byte)'9')
{
return true; // is digit
}
if ((byte)'a' <= b && b <= (byte)'z')
{
return true; // lowercase letter
}
if ((byte)'A' <= b && b <= (byte)'Z')
{
return true; // uppercase letter
}
switch (b)
{
case (byte)'-':
case (byte)'.':
case (byte)'_':
case (byte)'~':
case (byte)':':
case (byte)'!':
case (byte)'$':
case (byte)'&':
case (byte)'+':
return true;
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment