3

I'm using the C# System.IO class (framework 4.0) to compress an image pulled off the file system with FileDialog and then inserted into a SQL Server database as a varbinary(max) data type. The problem I'm having is when I pull the data out of the database and attempt to decompress I get the subject error with an additional message -- make sure you are passing in a gzip stream.

The code to get the file:

    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
        ....

     }

The compress method:

        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();
        }

The decompress method:

        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();
        }
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
pvitt
  • 1,005
  • 4
  • 14
  • 31
  • 3
    If you `Compress()` and `Decompress()` without having the database in between, how does that turn out? In other words: what you use the output of Compress directly for input of Decompress, does that work? In this way, you can find out if something is wrong in your code or in the `to/from database` code. – Sjips Nov 25 '14 at 21:19
  • 3
    Why do you compress a buffer filled with zero bytes? Use the debugger and inspect all variables at runtime. You'll find some strange stuff. – usr Nov 25 '14 at 21:23
  • I get the same error if I send output of Compress directly to Decompress. I've also checked the input to Compress and it is showing there is bytes – pvitt Nov 25 '14 at 21:29

1 Answers1

1

You need to insert a line and remove an other:

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
// ...

You are reading the image in your code, but something is in the wrong order:

// 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...

So you have saved the first part of the original file, not the compressed one (but the number of saved bytes corresponds with the number of compressed bytes). If you send this to the DB and read it back, the decompressor does not find it's Magic Number.

As an improvement, you can change these lines:

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

probably change to:

byte[] picbyte1 = File.ReadAllBytes(dlgOpen.FileName);
Sjips
  • 1,248
  • 2
  • 11
  • 22
  • 1
    He needs more than one line, `Read(` is not guaranteed to read the number of bytes you requested in `fs.Length`, you need to loop till you have filled your byte array offsetting the value you currently have as `0` in the second parameter with the `int` returned by Read (and subtracting `fs.Length` by the same offset too for the 3rd parameter). – Scott Chamberlain Nov 25 '14 at 22:14
  • @Scott: I think you are right. But me was learned that FileStreams are buffered and it can be done in this way (.Net 2.0 and later). I took it for granted (but never verified it). I did a test with this code and it worked. But your suggestion has to be kept in mind. If the Poster of this message still has trouble, this part needs attention. – Sjips Nov 25 '14 at 22:21
  • 1
    @Scott another time: I think `File.ReadAllBytes(dlgOpen.FileName);` would even work better. I've added it to the answer. – Sjips Nov 25 '14 at 22:28
  • Yes, I going to mention that in my first comment but the name of the function slipped my mind. – Scott Chamberlain Nov 25 '14 at 22:29