从SQL提取预格式化Base64到C#时的BOM编码问题排查
我需要将SQL Server数据库中存储的Base64编码图片提取到Blazor Server(.NET 8)应用,嵌入到HTML的<a>标签内。此前提议更换图片存储方式,但数据团队明确表示无法修改现有存储逻辑。
数据库中存储Base64字符串的列类型为VARCHAR(MAX),当前核心问题出在Microsoft.Data.SqlClient查询后的编码处理环节:直接通过SQL查询得到的字符串能正常作为img元素的src属性渲染图片,但C#代码查询返回的字符串却无法解码。经解码工具检测发现,C#输出的字符串存在字节顺序标记(BOM),手动修复BOM后即可正常解码为图片。我推测这是C#默认UTF16编码导致的,但尝试多种方法均无法有效移除BOM并得到可用的图片字符串。
相关代码
string invoiceNumber = "INV12345"; string query = "SELECT invoiceImage FROM dbo.receipts WHERE invoiceNumber = @InvoiceNumber"; using (SqlConnection connection = new SqlConnection(connectionString)) { using (SqlCommand command = new SqlCommand(query, connection)) { command.Parameters.AddWithValue("@InvoiceNumber", invoiceNumber); try { await connection.OpenAsync(); var result = await command.ExecuteScalarAsync(); if (result != null && result != DBNull.Value) { // 存在问题的字符串 string EmbedImage = result.ToString(); Console.WriteLine(EmbedImage); } else { Console.WriteLine("未找到对应发票号的图片"); } } catch (Exception ex) { Console.WriteLine("错误: " + ex.Message); } } }
已尝试的方案
- 使用
Convert.ToBase64String()方法(注:原代码存在编译错误,已修正为合理写法)
EmbedImage = Convert.ToBase64String(Encoding.UTF8.GetBytes(result.ToString()));
- 修剪BOM字符
EmbedImage = result.ToString().Trim(new char[]{'\uFEFF','\u200B'});
- 转换为字节数组再转回字符串
byte[] bytes = Convert.FromBase64String(result.ToString()); EmbedImage = Convert.ToBase64String(bytes);
解决方案
问题根源在于SQL Server的VARCHAR(MAX)列存储的是单字节编码的Base64字符串,而C#读取时可能因编码转换引入UTF-16 BOM或隐藏无效字符,以下是针对性解决方法:
1. 精准清理非Base64字符
Base64仅包含A-Za-z0-9+/=字符,直接用正则移除所有非合规字符,彻底清理BOM及其他无效内容:
string rawBase64 = result.ToString(); EmbedImage = Regex.Replace(rawBase64, @"[^A-Za-z0-9+/=]", string.Empty);
2. 按SQL默认编码读取字节数组
SQL Server的VARCHAR默认采用SQL_Latin1_General_CP1_CI_AS编码(对应Windows-1252),直接读取字节数组再转字符串,跳过可能出错的自动编码转换:
var resultBytes = await command.ExecuteScalarAsync() as byte[]; if (resultBytes != null) { string rawBase64 = Encoding.GetEncoding(1252).GetString(resultBytes); // 额外清理残留的BOM和空字符 EmbedImage = rawBase64.Trim(new char[] {'\uFEFF', '\u0000', '\u200B'}); }
3. 在SQL查询阶段直接清理BOM
如果C#端处理麻烦,可在SQL查询时直接移除开头的BOM前缀:
SELECT CASE WHEN LEFT(invoiceImage, 1) = NCHAR(65279) THEN SUBSTRING(invoiceImage, 2, LEN(invoiceImage)) ELSE invoiceImage END AS invoiceImage FROM dbo.receipts WHERE invoiceNumber = @InvoiceNumber
验证与使用
处理完成后,可将字符串复制到解码工具验证有效性,在Blazor中直接使用时需指定图片格式:
<img src="data:image/png;base64,@EmbedImage" alt="发票图片" />
(根据实际图片格式替换png为jpeg等)
内容的提问来源于stack exchange,提问作者Starship24

