编码和解码字符串
Convert 处理的是字节,所以要显式选择一种编码。只要数据会离开你的进程,几乎总是应该选 UTF-8:
using System.Text;
string enc = Convert.ToBase64String(Encoding.UTF8.GetBytes("héllo wörld"));
Console.WriteLine(enc); // aMOpbGxvIHfDtnJsZA==
string dec = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
Console.WriteLine(dec); // héllo wörld
Encoding.Unicode 是 UTF-16LE,会让 ASCII 文本的体积翻倍。只有当对方要求 UTF-16 时它才是正确的选择,例如 PowerShell 编码命令 格式。
一个常见的实际用途是 HTTP Basic Auth 请求头:
string token = Convert.ToBase64String(Encoding.UTF8.GetBytes("user:pass"));
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", token);
// Authorization: Basic dXNlcjpwYXNz
Basic Auth 请求头生成器 可以在浏览器中生成相同的值。
URL 安全的 Base64:.NET 9 的 Base64Url
.NET 9 新增了 System.Buffers.Text.Base64Url。它编码时不加填充,解码时有没有填充都行:
using System.Buffers.Text;
byte[] d = Encoding.UTF8.GetBytes("<<???>>");
Console.WriteLine(Base64Url.EncodeToString(d)); // PDw_Pz8-Pg
byte[] a = Base64Url.DecodeFromChars("PDw_Pz8-Pg"); // <<???>>
byte[] b = Base64Url.DecodeFromChars("PDw_Pz8-Pg=="); // <<???>>
它不接受标准字母表:Base64Url.DecodeFromChars("PDw/Pz8+Pg") 会抛出 FormatException。另外还有基于 span 的重载(EncodeToChars、DecodeFromUtf8、TryDecodeFromChars),可以避免内存分配。
在没有 Microsoft.Bcl.Memory 包的旧运行时上,就手动转换:
string url = Convert.ToBase64String(d)
.TrimEnd('=').Replace('+', '-').Replace('/', '_'); // PDw_Pz8-Pg
解码时需要反向替换,并把长度补齐到 4 的倍数:
static byte[] FromBase64Url(string s)
{
s = s.Replace('-', '+').Replace('_', '/');
s = s.PadRight(s.Length + (4 - s.Length % 4) % 4, '=');
return Convert.FromBase64String(s);
}
Encoding.UTF8.GetString(FromBase64Url("PDw_Pz8-Pg")); // <<???>>
ASP.NET Core 项目也可以改用 Microsoft.AspNetCore.WebUtilities 中的 WebEncoders.Base64UrlEncode。关于这两种字母表的更多内容,见 Base64 与 Base64URL 的区别。
不抛异常地校验输入
Convert.FromBase64String 遇到无效输入时会抛出 System.FormatException。在 .NET 9 上,几乎所有情况的错误信息都一样:
The input is not a valid Base-64 string as it contains a non-base 64 character,
more than two padding characters, or an illegal character among the padding characters.
对于 PDw_Pz8-Pg==(URL 安全)、aGk=!!(无关字符)、aGk(无填充)、a 和 aGk==,我们得到的都是这条信息。它接受空白(" aGk= " 和 "aGVs\nbG8K" 都能正常解码),也接受不为零的填充位(aGl=)。
要在热点路径上避免异常,可以用带缓冲区的 Try 版本,或者 Base64.IsValid(.NET 8 及以上):
Span<byte> buf = stackalloc byte[16];
Convert.TryFromBase64String("aGk=", buf, out int n); // True, n = 2
Convert.TryFromBase64String("aGk", buf, out n); // False, n = 0
System.Buffers.Text.Base64.IsValid("aGk="); // True
System.Buffers.Text.Base64.IsValid("aGk"); // False
Base64Url.IsValid("PDw_Pz8-Pg"); // True
缓冲区必须足够容纳解码后的输出,否则即使输入有效,TryFromBase64String 也会返回 false。缓冲区大小取 (s.Length * 3) / 4。对于解不开的字符串,可以借助 Base64 校验工具 和 错误指南。
换行(MIME)
Base64FormattingOptions.InsertLineBreaks 会在 76 个字符处用 \r\n 换行,末尾不带换行符:
string mime = Convert.ToBase64String(
Encoding.ASCII.GetBytes(new string('x', 60)),
Base64FormattingOptions.InsertLineBreaks);
// "eHh4eHh4...eHh4" (76 chars) + "\r\n" + "eHh4"
FromBase64String 无需任何改动就能解码这种分行格式。MIME Base64 编码器 生成的布局与此相同。
编码和解码文件
Console.WriteLine(Convert.ToBase64String(File.ReadAllBytes("hello.txt"))); // aGVsbG8K
File.WriteAllBytes("copy.txt", Convert.FromBase64String("aGVsbG8K"));
用 CryptoStream 处理大文件
ToBase64Transform 和 FromBase64Transform 可以接入 CryptoStream,让内存占用保持平稳。别被命名空间误导,这里不涉及任何加密:
using System.Security.Cryptography;
using (var input = File.OpenRead("big.bin"))
using (var output = File.Create("big.b64"))
using (var b64 = new CryptoStream(output, new ToBase64Transform(), CryptoStreamMode.Write))
{
input.CopyTo(b64);
}
using (var input = new CryptoStream(File.OpenRead("big.b64"),
new FromBase64Transform(), CryptoStreamMode.Read))
using (var output = File.Create("big.out"))
{
input.CopyTo(output);
}
释放 CryptoStream 时才会写出最后一个块和填充;漏掉这一步,输出就会被截断。在一个 300 KB 的测试文件上,两个方向的结果都与内存中处理的结果一致。想快速手动检查,可以用 Base64 转文件 打开结果。
常见陷阱
- Data URI 前缀:
data:image/png;base64,包含:和;,会触发FormatException。把逗号及其之前的内容全部去掉。 - 把 URL 安全令牌交给
Convert:JWT 的各部分会报上面那条通用错误。请用Base64Url或 JWT 解码器。 Encoding.Default:在 .NET Framework 上它是 ANSI 代码页,在 .NET Core 及以后的版本中是 UTF-8。代码在两者之间迁移后,非 ASCII 文本的输出会改变。- Base64 不是加密:用 Base64“保护”的连接字符串,对任何人来说都是明文。见 编码与加密的区别。
版本说明
- .NET Framework 1.0 及以上:
Convert.ToBase64String、FromBase64String、ToBase64Transform。 - .NET Core 2.1 及以上:span 重载
Convert.TryFromBase64String、TryToBase64Chars,以及面向 UTF-8 的System.Buffers.Text.Base64类。 - .NET 8:
Base64.IsValid。 - .NET 9:
Base64Url,旧的目标框架也可以通过Microsoft.Bcl.Memory使用。
常见问题
如何解决 The input is not a valid Base-64 string 报错?
检查是否有 URL 安全字符(-、_)、缺失的填充以及 Data URI 前缀。Convert.FromBase64String 只接受填充正确的标准字母表。处理令牌时,在 .NET 9 上请用 Base64Url.DecodeFromChars,它带不带填充都能接受。
在 .NET 8 或 .NET Framework 上能用 Base64Url 吗?
Base64Url 内置于 .NET 9 及更高版本。对于更旧的目标框架,微软在 Microsoft.Bcl.Memory 包中提供了同一个类型。如果不用这个包,就对标准输出调用 TrimEnd('=') 和两次 Replace 来转换。
为什么对文本做 Base64 时不该用 Encoding.Unicode?
Encoding.Unicode 是 UTF-16LE,所以 hi 会变成 aABpAA==,而不是 aGk=。除非接收方要求 UTF-16(比如 PowerShell 的 -EncodedCommand),否则请用 Encoding.UTF8。