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

使用ICSharpCode.SharpZipLib.Zip压缩SQL FileStream文件下载时异常解决

解决SqlFileStream读取文件生成Zip包时的CloseEntry异常问题

你遇到的这个异常"size was 845941, but I expected 16 at ICSharpCode.SharpZipLib.Zip.ZipOutputStream.CloseEntry()",核心原因一眼就能看出来:你给ZipEntry设置的Size值完全不是文件的实际大小。

看你代码里的这行:

zipEntry.Size = model123.serverTxn.Length;

这里的serverTxn是从GET_FILESTREAM_TRANSACTION_CONTEXT()获取的事务上下文二进制数据,它的长度只有16字节左右,但你实际从SqlFileStream读取的文件大小是845941字节。SharpZipLib会严格校验写入的字节数和预先设置的ZipEntry.Size是否一致,一旦不匹配就会抛出这个异常。

修复步骤

  1. 修改SQL查询,获取文件真实大小
    在你的SELECT语句中添加LEN(FileData) AS FileSize,这样就能拿到FileStream中存储的文件实际字节数:

    SELECT FileData.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(), FileType, LEN(FileData) AS FileSize
    FROM MyFiles WHERE FileId = @FileId
    
  2. 读取文件大小并赋值给ZipEntry
    在读取SqlDataReader时,把FileSize字段取出来,再设置给zipEntry.Size:

    model123.fileSize = rdr.GetInt64(3); // 用Int64适配GB级大文件
    // ...
    zipEntry.Size = model123.fileSize;
    
  3. 优化代码细节(可选但推荐)

    • 把TransactionScope移到外层,避免每个文件都创建一次事务,提升性能
    • 去掉手动调用的sfs.Close(),using语句会自动释放资源

修改后的完整代码

string zipFileName = "ZipName.zip";
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", $"attachment; fileName={zipFileName}"); // 加上attachment确保浏览器触发下载
byte[] buffer = new byte[4096];
ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream);
zipOutputStream.SetLevel(3);
string cs = System.Configuration.ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;

// 事务放到外层,减少重复创建开销
using (TransactionScope ts = new TransactionScope())
{
    using (System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(cs))
    {
        conn.Open();
        foreach (Filec file1 in Files)
        {
            StreamModel model123 = new StreamModel();
            const string SelectTSql = @"
                SELECT FileData.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT(), FileType, LEN(FileData) AS FileSize
                FROM MyFiles WHERE FileId = @FileId";
            
            using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand(SelectTSql, conn))
            {
                cmd.Parameters.Add("@FileId", System.Data.SqlDbType.Int).Value = Convert.ToInt32(file1.FileId);
                using (System.Data.SqlClient.SqlDataReader rdr = cmd.ExecuteReader())
                {
                    rdr.Read();
                    model123.serverPath = rdr.GetSqlString(0).Value;
                    model123.serverTxn = rdr.GetSqlBinary(1).Value;
                    model123.filetype = rdr.GetSqlString(2).Value;
                    model123.fileSize = rdr.GetInt64(3); // 获取真实文件大小
                    rdr.Close();
                }
            }

            ZipEntry zipEntry = new ZipEntry(ZipEntry.CleanName(file1.FileName));
            zipEntry.Size = model123.fileSize; // 设置正确的文件大小
            zipOutputStream.PutNextEntry(zipEntry);
            
            byte[] buffer3 = new byte[4096];
            using (System.Data.SqlTypes.SqlFileStream sfs = new System.Data.SqlTypes.SqlFileStream(
                model123.serverPath, 
                model123.serverTxn, 
                FileAccess.Read))
            {
                int bytesRead;
                while ((bytesRead = sfs.Read(buffer3, 0, buffer3.Length)) > 0)
                {
                    zipOutputStream.Write(buffer3, 0, bytesRead);
                }
            }
            zipOutputStream.CloseEntry(); // 现在不会再抛出异常了
        }
        ts.Complete();
    }
}

zipOutputStream.Close();
Response.Flush();
Response.End();

额外说明

如果你不想在SQL里查询文件大小,也可以在打开SqlFileStream后直接通过sfs.Length获取真实大小,再赋值给zipEntry.Size,两种方式都能解决问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:49:24