You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于.NET 2.0无外部库解压Zip获取XML,适配SQL Server 2008 CLR

好消息是,在.NET 2.0环境下不用外部库解压ZIP是可行的——虽然没有后来.NET版本里的ZipArchive那么方便,但我们可以手动解析ZIP文件的底层格式,配合.NET 2.0自带的DeflateStream来完成解压。下面针对你的SQL Server 2008 CLR场景给出具体方案:

方案一:纯.NET 2.0内存解压(推荐)

这个方案完全依赖.NET 2.0自带的类库,不需要外部依赖,也能避免文件系统操作(更适合SQL CLR的权限限制)。核心思路是手动解析ZIP文件的Local File Header结构,提取每个文件的压缩数据后用DeflateStream解压(ZIP最常用的压缩方式就是Deflate)。

实现代码

首先定义一个简单的类来存储ZIP内的文件信息:

public class ZipEntry
{
    public string FileName { get; set; }
    public byte[] Data { get; set; }
}

然后实现解压方法,直接从ZIP的字节数组中提取所有文件:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;

public static class ZipExtractor
{
    public static List<ZipEntry> ExtractZip(byte[] zipData)
    {
        var entries = new List<ZipEntry>();
        
        using (var zipStream = new MemoryStream(zipData))
        using (var reader = new BinaryReader(zipStream))
        {
            while (zipStream.Position < zipStream.Length)
            {
                // 读取ZIP文件的Local File Header签名(固定为0x04034B50)
                uint headerSignature = reader.ReadUInt32();
                if (headerSignature != 0x04034B50)
                {
                    // 遇到非Local File Header(比如Central Directory或结束记录),终止循环
                    break;
                }

                // 跳过不需要的头字段:版本、标志等
                reader.ReadUInt16(); // 解压所需最低版本
                reader.ReadUInt16(); // 通用标志位
                ushort compressionMethod = reader.ReadUInt16(); // 压缩方法
                reader.ReadUInt16(); // 文件修改时间
                reader.ReadUInt16(); // 文件修改日期
                reader.ReadUInt32(); // CRC32校验值
                uint compressedSize = reader.ReadUInt32(); // 压缩后大小
                uint uncompressedSize = reader.ReadUInt32(); // 解压后大小
                ushort fileNameLength = reader.ReadUInt16(); // 文件名长度
                ushort extraFieldLength = reader.ReadUInt16(); // 额外字段长度

                // 读取文件名
                string fileName = Encoding.UTF8.GetString(reader.ReadBytes(fileNameLength));
                // 跳过额外字段
                reader.ReadBytes(extraFieldLength);

                // 读取压缩数据并解压
                byte[] compressedBytes = reader.ReadBytes((int)compressedSize);
                byte[] uncompressedBytes;

                if (compressionMethod == 0)
                {
                    // 文件未压缩,直接使用原数据
                    uncompressedBytes = compressedBytes;
                }
                else if (compressionMethod == 8)
                {
                    // Deflate压缩:注意跳过ZIP格式额外的2字节头
                    using (var compressedStream = new MemoryStream(compressedBytes, 2, compressedBytes.Length - 2))
                    using (var deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
                    using (var outputStream = new MemoryStream())
                    {
                        deflateStream.CopyTo(outputStream);
                        uncompressedBytes = outputStream.ToArray();
                    }
                }
                else
                {
                    throw new NotSupportedException($"不支持的压缩方式:{compressionMethod}");
                }

                entries.Add(new ZipEntry { FileName = fileName, Data = uncompressedBytes });
            }
        }

        return entries;
    }
}

在你的CLR代码中使用

你可以去掉原有的临时文件写入逻辑,直接处理下载到的byte[]:

byte[] zipBinary = GetStreamFileResult(..);
var zipEntries = ZipExtractor.ExtractZip(zipBinary);

foreach (var entry in zipEntries)
{
    // 筛选XML文件
    if (entry.FileName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        // 将字节数组转为XML字符串
        string xmlContent = Encoding.UTF8.GetString(entry.Data);
        // 接下来可以将XML插入SQL表,或进行其他处理
        // ...
    }
}

注意事项

  1. 权限适配:如果全程在内存中处理(不读写文件系统),你的CLR程序集设置为SAFE权限即可;如果需要写入文件,需设置为EXTERNAL_ACCESS。
  2. ZIP格式支持:这个实现处理的是标准ZIP格式,不支持加密ZIP、分卷ZIP或ZIP64(大于4GB的文件),但对于XML文件场景足够使用。
  3. 文件名编码:如果遇到非UTF8编码的文件名乱码,可以将Encoding.UTF8替换为Encoding.GetEncoding(437)(ZIP默认的DOS编码)。

方案二:依赖Windows系统工具(备选)

如果你的SQL Server服务器允许执行外部进程,也可以调用Windows自带的expand.exe工具来解压ZIP。但这个方案需要更高的权限,且依赖系统环境,稳定性不如方案一。

示例代码

string tempZipPath = Path.Combine(Path.GetTempPath(), "temp.zip");
string extractDir = Path.Combine(Path.GetTempPath(), "extracted_xmls");
Directory.CreateDirectory(extractDir);

// 写入临时ZIP文件
File.WriteAllBytes(tempZipPath, varbinary);

// 调用expand.exe解压
string expandExePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "expand.exe");
System.Diagnostics.Process.Start(expandExePath, $"\"{tempZipPath}\" \"{extractDir}\\*\"")?.WaitForExit();

// 读取解压后的XML文件
foreach (string xmlFile in Directory.GetFiles(extractDir, "*.xml"))
{
    string xmlContent = File.ReadAllText(xmlFile);
    // 处理XML...
}

// 清理临时文件
File.Delete(tempZipPath);
Directory.Delete(extractDir, true);

注意事项

  • 你的CLR程序集必须设置为UNSAFE权限才能执行外部进程。
  • 确保expand.exe存在于服务器的System目录中(Windows系统默认自带)。
  • 临时文件路径需要确保SQL Server服务账户有读写权限。

内容的提问来源于stack exchange,提问作者Manos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:35:53