流式读取SQL Server nvarchar(max)生成带BOM的UTF-16压缩文件
解决方案:流式生成带BOM的UTF-16压缩文件
SQL Server的nvarchar(max)字段本身采用UTF-16LE编码存储,直接用SqlDataReader.GetBytes读取的字节流就是合法的UTF-16LE数据,仅缺少UTF-16LE的字节顺序标记(BOM:0xFF 0xFE)。我们只需要在写入Zip内文件流的最开始先写入BOM,再流式传输数据即可,既避免内存耗尽,又满足导入要求。
修正后的代码
using (SqlDataReader Rd = DbMan.GetDbReader(command, CommandBehavior.SequentialAccess) as SqlDataReader) { using (MemoryStream MSZip = new MemoryStream()) { // 设置leaveOpen: true,后续若要操作MSZip需保持流打开 using (var Zip = new ZipArchive(MSZip, ZipArchiveMode.Create, leaveOpen: true)) { while (Rd.Read()) { // 修正C#插值字符串写法 var fileName = $"MyFile{Rd.GetString(0)}.json"; var FileJsonInsideZip = Zip.CreateEntry(fileName, CompressionLevel.Fastest); using (var StreamFileInsideZip = FileJsonInsideZip.Open()) { // 第一步:写入UTF-16LE的BOM,这是SQL Server识别Unicode文件的关键 byte[] bom = Encoding.Unicode.GetPreamble(); StreamFileInsideZip.Write(bom, 0, bom.Length); // 第二步:流式读取SQL字段字节,逐段写入Zip文件流 byte[] buffer = new byte[8192]; // 8192是推荐的IO缓冲区大小,匹配系统块大小 long offset = 0; long read; while ((read = Rd.GetBytes(1, offset, buffer, 0, buffer.Length)) > 0) { StreamFileInsideZip.Write(buffer, 0, (int)read); offset += read; } } } } // 示例:将生成的Zip流保存到文件 // MSZip.Position = 0; // using (var fileStream = new FileStream("output.zip", FileMode.Create)) // { // MSZip.CopyTo(fileStream); // } } }
关键说明
- BOM的必要性:SQL Server导入时依赖UTF-16的BOM识别文件编码,
Encoding.Unicode.GetPreamble()会返回标准的UTF-16LE BOM字节数组。 - 内存控制:保持
CommandBehavior.SequentialAccess模式,配合固定大小缓冲区逐段读取,避免一次性加载1GB数据到内存。 - 编码一致性:
nvarchar存储的就是UTF-16LE数据,直接读取字节流无需额外编码转换,保证效率和正确性。
内容的提问来源于stack exchange,提问作者matti157
相关产品推荐
相关产品推荐

