解压gzip流时出错--GZip头中的幻数不正确。

3
我正在使用C#的System.IO类(框架4.0)来压缩从文件系统中获取的图像,并使用FileDialog插入到SQL Server数据库中作为varbinary(max)数据类型。 我遇到的问题是,当我从数据库中提取数据并尝试解压缩时,我会得到一个错误,附加信息为-确保您传递了gzip流。
获取文件的代码:
    OpenFileDialog dlgOpen = new OpenFileDialog();

    if (dlgOpen.ShowDialog() == DialogResult.OK)
    {
        FileStream fs = File.OpenRead(dlgOpen.FileName);
        byte[] picbyte1 = new byte[fs.Length];
        byte[] picbyte = Compress(picbyte1);
        fs.Read(picbyte, 0, System.Convert.ToInt32(picbyte.Length));
        String ImageName = dlgOpen.FileName;
        //String bs64OfBytes = Convert.ToBase64String(picbyte);
        fs.Close();
        //additional code inserts into database
        ....

     }

压缩方法:
        private static byte[] Compress(byte[] data)
        {
            var output = new MemoryStream();
            using (var gzip = new GZipStream(output, CompressionMode.Compress, true))
            {
                gzip.Write(data, 0, data.Length);
                gzip.Close();
            }
            return output.ToArray();
        }

解压缩方法:
        private static byte[] Decompress(byte[] data)
        {
            var output = new MemoryStream();
            var input = new MemoryStream();
            input.Write(data, 0, data.Length);
            input.Position = 0; 

            using (var gzip = new GZipStream(input, CompressionMode.Decompress, true))
            {
                var buff = new byte[64];//also used 32
                var read = gzip.Read(buff, 0, buff.Length);//error occurs here

                while (read > 0)
                {
                    output.Write(buff, 0, read);
                    read = gzip.Read(buff, 0, buff.Length);
                }

                gzip.Close();
            }
            return output.ToArray();
        }

3
如果在数据库中间没有进行 Compress()Decompress() 操作,那么会出现什么情况?换句话说,如果直接将 Compress 的输出用作 Decompress 的输入,是否有效?通过这种方式,您可以确定代码或 to/from database 代码的错误。请注意,不要更改原文意思。 - Sjips
3
为什么要压缩一个由零字节填充的缓冲区?在运行时使用调试器检查所有变量,你会发现一些奇怪的东西。 - usr
如果我直接将压缩的输出发送到解压缩,我会得到相同的错误。我还检查了压缩的输入,它显示有字节。 - pvitt
1个回答

1
你需要插入一行并删除另一行:

你需要插入一行并删除另一行:

FileStream fs = File.OpenRead(dlgOpen.FileName);
byte[] picbyte1 = new byte[fs.Length];
fs.Read(picbyte1, 0, (int)fs.Length); // <-- Add this one
byte[] picbyte = Compress(picbyte1);
// fs.Read(picbyte, 0, System.Convert.ToInt32(picbyte.Length)); // <-- And remove this one
// ...

您正在代码中读取图像,但是某些内容顺序不正确:
// Original but incorrect sequence
FileStream fs = File.OpenRead(dlgOpen.FileName); // Open the file
byte[] picbyte1 = new byte[fs.Length]; // Assign the array
byte[] picbyte = Compress(picbyte1); // Compress the assigned array, but there is no contents...
fs.Read(picbyte, 0, System.Convert.ToInt32(picbyte.Length)); // You save the file to the already compressed bytes...

所以你保存的是原始文件的第一部分,而不是压缩后的文件(但保存的字节数与压缩后的字节数相同)。如果你将其发送到数据库并读取回来,解压程序将无法找到它的魔数。

作为改进,你可以更改这些行:

FileStream fs = File.OpenRead(dlgOpen.FileName);
byte[] picbyte1 = new byte[fs.Length];
fs.Read(picbyte1, 0, (int)fs.Length); // line that I suggested to add

可能会更改为:

byte[] picbyte1 = File.ReadAllBytes(dlgOpen.FileName);

1
他需要不止一行代码,“Read()”不能保证在“fs.Length”中读取所请求的字节数,您需要循环直到填充您的字节数组为止,将第二个参数中当前值为“0”的值偏移为由读取返回的“int”(还需将“fs.Length”减去相同的偏移量作为第三个参数)。 - Scott Chamberlain
@Scott:我认为你是对的。但是我学到的是FileStream是有缓冲区的,可以用这种方式实现(.Net 2.0及更高版本)。我认为这是理所当然的(但从未验证过)。我用这段代码进行了测试,它起作用了。但是你的建议必须记在心里。如果发帖人仍然有问题,这部分需要注意。 - Sjips
1
@Scott 再次提醒:我认为 File.ReadAllBytes(dlgOpen.FileName); 会更好用。我已经将其添加到答案中。 - Sjips
是的,我打算在我的第一条评论中提到这个,但是函数的名称从我的脑海中滑落了。 - Scott Chamberlain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接