MySQL 和 MariaDB:TO_BASE64 与 FROM_BASE64
SELECT TO_BASE64('héllo wörld');
-- aMOpbGxvIHfDtnJsZA== (connection charset utf8mb4)
SELECT CONVERT(FROM_BASE64('aMOpbGxvIHfDtnJsZA==') USING utf8mb4);
-- héllo wörld
TO_BASE64 按字符串所属字符集的字节进行编码。使用 utf8mb4 时得到的是 UTF-8;latin1 列给出的是 Latin-1 字节,所以 TO_BASE64(CONVERT('héllo' USING latin1)) 返回 aOlsbG8=,而不是 aMOpbGxv。FROM_BASE64 返回二进制字符串;CONVERT(... USING utf8mb4) 把它转换回文本。
TO_BASE64 和 MIME 一样,在 76 个字符处换行:
SELECT LENGTH(TO_BASE64(REPEAT('x', 60))); -- 81 (80 chars + 1 newline)
SELECT REPLACE(TO_BASE64(REPEAT('x', 60)), '\n', ''); -- single line, 80 chars
无效输入不会报错,FROM_BASE64 会返回 NULL:
| 输入 | 结果 |
|---|---|
aGk= | hi |
aGk | NULL(无填充) |
aGk=!! | NULL |
PDw_Pz8-Pg== | NULL(URL 安全) |
a | NULL |
aGl= | hi(接受不为零的填充位) |
'aGVs\nbG8K'、'aGVs bG8K' | hello\n(忽略空白) |
用字符串函数进行 URL 安全转换:
SELECT TRIM(TRAILING '=' FROM REPLACE(REPLACE(TO_BASE64('<<???>>'), '+', '-'), '/', '_'));
-- PDw_Pz8-Pg
SELECT FROM_BASE64(CONCAT(
REPLACE(REPLACE('PDw_Pz8-Pg', '-', '+'), '_', '/'),
REPEAT('=', (4 - LENGTH('PDw_Pz8-Pg') % 4) % 4)));
-- <<???>>
对于较长的值,编码函数外还要再包一层 REPLACE(..., '\n', '')。这两个函数从 MySQL 5.6 起就有,MariaDB 中也有。
PostgreSQL:encode 与 decode
PostgreSQL 处理的是 bytea。用 convert_to 转换文本,再用 convert_from 转回来:
SELECT encode(convert_to('héllo wörld', 'UTF8'), 'base64');
-- aMOpbGxvIHfDtnJsZA==
SELECT convert_from(decode('aMOpbGxvIHfDtnJsZA==', 'base64'), 'UTF8');
-- héllo wörld
和 MySQL 一样,encode(..., 'base64') 在 76 个字符处换行;psql 会在行尾用 + 表示换行。可以用 replace(encode(x, 'base64'), E'\n', '') 去掉。
与 MySQL 不同的是,无效输入会报错:
decode('aGk', 'base64')
ERROR: invalid base64 end sequence
HINT: Input data is missing padding, is truncated, or is otherwise corrupted.
decode('PDw_Pz8-Pg==', 'base64')
ERROR: invalid symbol "_" found while decoding base64 sequence
aGk=!! 会以 invalid symbol "!" 失败,a 则报 invalid base64 end sequence。空白(包括换行符和空格)会被忽略,aGl= 也会被接受。
PostgreSQL 17 和 18 没有 base64url 格式(报 unrecognized encoding: "base64url"),所以要用 translate 转换:
SELECT translate(rtrim(encode('<<???>>'::bytea, 'base64'), '='), '+/', '-_');
-- PDw_Pz8-Pg
SELECT convert_from(decode(
rpad(translate('PDw_Pz8-Pg', '-_', '+/'), (length('PDw_Pz8-Pg') + 3) / 4 * 4, '='),
'base64'), 'UTF8');
-- <<???>>
要在不中断事务的前提下做校验,可以把 decode 包进一个 PL/pgSQL 函数,用 EXCEPTION WHEN others 块返回 NULL。
SQL Server:XML base64Binary 技巧
SQL Server 2025 之前没有 Base64 函数。XML 类型可以在 varbinary 和 xs:base64Binary 之间转换,而后者就是 Base64:
-- encode
DECLARE @bin varbinary(max) = CAST('hi' AS varbinary(max));
SELECT CAST('' AS xml).value('xs:base64Binary(sql:variable("@bin"))', 'varchar(max)');
-- aGk=
-- decode
DECLARE @b64 varchar(max) = 'aGk=';
SELECT CAST('' AS xml).value('xs:base64Binary(sql:variable("@b64"))', 'varbinary(max)');
-- 0x6869
FOR XML ... BINARY BASE64 和 FOR JSON 也会把 varbinary 列输出为 Base64:SELECT CAST('hi' AS varbinary(max)) AS bin FOR JSON PATH 返回 [{"bin":"aGk="}]。XML 转换不会换行:60 个字节输出为一个 80 字符的单行字符串。
XML 方法需要 QUOTED_IDENTIFIER ON。sqlcmd 默认是关闭的,会报 Msg 1934: SELECT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'。请加上 -I 参数,或者执行 SET QUOTED_IDENTIFIER ON。
SQL Server 中的文本编码
nvarchar 是 UTF-16LE,所以 CAST(N'héllo wörld' AS varbinary(max)) 会编码为 aADpAGwAbABvACAAdwD2AHIAbABkAA==,其他系统解码后字母之间会有空隙。要得到 UTF-8(SQL Server 2019+),先经过一个 UTF-8 排序规则:
DECLARE @bin varbinary(max) = CAST(CAST(N'héllo wörld'
COLLATE Latin1_General_100_CI_AS_SC_UTF8 AS varchar(max)) AS varbinary(max));
-- encodes to aMOpbGxvIHfDtnJsZA==
把 UTF-8 解码回来,需要一个 UTF-8 的目标。在默认排序规则为 SQL_Latin1_General_CP1_CI_AS 的数据库中,直接 CAST(@raw AS varchar(max)) 会显示乱码 héllo wörld。把这些字节插入声明为 varchar(max) COLLATE Latin1_General_100_CI_AS_SC_UTF8 的列中,得到的才是 héllo wörld。对于 UTF-16 数据,CAST(@raw AS nvarchar(max)) 就够了。
错误
使用字符串字面量时,错误的 Base64 会在编译时失败:Msg 9319: XQuery [value()]: Static simple type validation: Invalid simple type value 'aGk'.。同样的输入通过 sql:variable 传入时,则会返回 NULL。缺少填充、URL 安全字符(PDw_Pz8-Pg==)和不为零的填充位(aGl=)都会被拒绝;字符串中的换行符则会被接受。
SQL Server 2025:BASE64_ENCODE 与 BASE64_DECODE
SQL Server 2025(17.x)和 Azure SQL 加入了真正的函数,还带有 URL 安全标志:
SELECT BASE64_ENCODE(0x3C3C3F3F3F3E3E); -- PDw/Pz8+Pg==
SELECT BASE64_ENCODE(0x3C3C3F3F3F3E3E, 1); -- PDw_Pz8-Pg (url_safe, no padding)
SELECT BASE64_DECODE('PDw_Pz8-Pg'); -- 0x3C3C3F3F3F3E3E
BASE64_ENCODE 只接受 varbinary;传入 varchar 会报 Msg 8116: Argument data type varchar(max) is invalid for argument 1 of base64_encode function。BASE64_DECODE 接受两种字母表,带不带填充都行,也接受 aGl=。aGk=!! 或 a 这类无效输入会引发 Msg 9803: Invalid data for type "Base64Decode"。
各数据库通用的陷阱
- 输入的字符集:同样的文本,在
latin1列、utf8mb4列和nvarchar列中得到的 Base64 各不相同。请统一使用 UTF-8 字节,并显式转换。 - 隐藏的换行符:MySQL 和 PostgreSQL 会在 76 个字符处换行。JSON 的使用方和 HTTP 头会因为夹在中间的
\n而出错。 - 悄无声息的
NULL:MySQL 和 SQL Server 的 XML 技巧在输入错误时返回NULL,而JOIN或WHERE会默默地把它过滤掉。 - 大数据值:在查询中对 BLOB 编码,会让网络上传输的数据多出 33%。要手动检查存储的值,可以查询出 Base64,再用 Base64 转文件 或 Base64 转 hex 打开。
常见问题
为什么 MySQL 的 FROM_BASE64 返回 NULL?
对于无效输入,MySQL 返回 NULL 而不是报错:缺少填充、URL 安全的 - 或 _,或者有垃圾字符。请用 Base64 校验工具 检查输入,并在解码前用 REPLACE 修正 URL 安全的字符串。
为什么 MySQL 的 TO_BASE64 输出中有换行?
TO_BASE64 遵循 MIME 惯例,每 76 个字符插入一个换行符。用 REPLACE(TO_BASE64(x), '\n', '') 包一层,就能得到单行输出。
SQL Server 有 Base64 函数吗?
SQL Server 2025 和 Azure SQL 提供了 BASE64_ENCODE 和 BASE64_DECODE。更早的版本没有这类函数,但 XML 的 xs:base64Binary 转换可以完成同样的工作。
应该把文件以 Base64 形式存进数据库吗?
通常不应该。二进制列(BLOB、bytea、varbinary)存储同样的数据要小约 25%,还省去了转换。只有在把数据发送给需要文本的系统时才进行编码。见 Base64 体积开销。