11

i'm querying a webservice in c# 4.0, which provides me with a string compressed by php's gzcompress(). Now i need to decompress this string in c#. I tried several ways including

but everytime i get an "Missing Magic Number" exception.

Can someone provide me with some hints?

Thank you

Edit 1:

My latest try:

public static string Decompress(string compressed) {
    byte[] compressedBytes = Encoding.ASCII.GetBytes(compressed);
    MemoryStream mem = new MemoryStream(compressedBytes);
    GZipStream gzip = new GZipStream(mem, CompressionMode.Decompress);
    StreamReader reader = new StreamReader(gzip);
    return reader.ReadToEnd();
}
Community
  • 1
  • 1
KitKat
  • 111
  • 1
  • 4

2 Answers2

11

Well, there you go, with a little help from @boas.anthro.mnsu.edu:

using (var mem = new MemoryStream())
{
    mem.Write(new byte[] { 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0, 8);
    mem.Write(inputBytes, 0, inputBytes.Length);

    mem.Position = 0;

    using (var gzip = new GZipStream(mem, CompressionMode.Decompress))
    using (var reader = new StreamReader(gzip))
    {
        Console.WriteLine(reader.ReadToEnd());
    }
}

The trick is to add a magic header. Note that this does not work with SharpZipLib. It complains that there is not footer. However, the .NET decompresser works perfectly.

One more thing. The comment concerning the ASCII.GetBytes() is correct: your input isn't ASCII. I achieved this result with the following:

// From PHP:

<?php echo base64_encode(gzcompress("Hello world!")); ?>

// In C#:

string input = "eJzzSM3JyVcozy/KSVEEAB0JBF4=";

byte[] inputBytes = Convert.FromBase64String(input);

With the extra base64 encoding and decoding, this works perfectly.

If you can't use base64 encoding, you need the raw stream from the PHP page. You can get this using the GetResponseStream():

 var request = WebRequest.Create("http://localhost/page.php");

 using (var response = request.GetResponse())
 using (var mem = response.GetResponseStream())
 {
     // Decompression code from above.
 }
Pieter van Ginkel
  • 29,160
  • 8
  • 71
  • 111
  • If you have control over the PHP side, using `gzencode` in place of `gzcompress` will produce output with the correct GZIP header and footer rather than leaving you to fake it on the C# side. – stevemegson Nov 02 '10 at 18:45
  • Sadly i don't have any controll about the PHP side. Will ASCII.GetBytes() still be okay? – KitKat Nov 02 '10 at 19:56
  • No. The thing is, you're getting the data from the PHP page somewhere. I believe you are getting that as a string. That is incorrect and you should get it as a `byte[]` or a `(Memory)Stream`. I've added a sample to the answer. If you don't know how to do this and the example does not suffice, please add the code which you use to read out the PHP page to the article and I'll give a suggestion on how you could change it. – Pieter van Ginkel Nov 02 '10 at 20:06
1

I want to extend Peter's answer. PHP also may compress with Deflate algorithm. In this case you should use DeflateStream instead of GZipStream and remove first 2 bytes (HEX: 78 9C) DeflateStream not working with buffer processed from PHP implementation

 private static byte[] Decompress(byte[] data)
{
  using (var compressedStream = new MemoryStream(data.Skip(2).ToArray()))
  using (var zipStream = new DeflateStream(compressedStream, CompressionMode.Decompress))
  using (var resultStream = new MemoryStream())
  {
    zipStream.CopyTo(resultStream);
    return resultStream.ToArray();
  }
}