1

I have a string in Java, that I want to encode with GZip, send the encoded string over a socket to an iOS app and decode the string there. This is not working, because I don't get the correct string when I decode it on the app side (instead, I get 3 question marks when I do a NSLog of the data). In Java, I'm using the GZIPOutputStream to encode the data and in iOS, I'm using a category on NSData to decode the data.

This is what I have in Java:

// gzip the string
sb = new StringBuilder("the quick brown fox jumps over the lazy dog");
ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outBytes);
gzip.write(sb.toString().getBytes());
gzip.finish();
gzip.close();
outBytes.close();
System.out.println("Gzipped string: " + outBytes.toString());

out.print(outBytes.toString());
out.flush();

Where out is a PrintWriter with the output stream of my socket.

In iOS, I'm using the following category to decode the data:

+ (NSData *)gzipDeflate:(NSData*)data
{
    if ([data length] == 0) return data;

    z_stream strm;

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in=(Bytef *)[data bytes];
    strm.avail_in = [data length];

    // Compresssion Levels:
    //   Z_NO_COMPRESSION
    //   Z_BEST_SPEED
    //   Z_BEST_COMPRESSION
    //   Z_DEFAULT_COMPRESSION

    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;

    NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chunks for expansion

    do {
        if (strm.total_out >= [compressed length])
            [compressed increaseLengthBy: 16384];

        strm.next_out = [compressed mutableBytes] + strm.total_out;
        strm.avail_out = [compressed length] - strm.total_out;

        deflate(&strm, Z_FINISH);

    } while (strm.avail_out == 0);

    deflateEnd(&strm);

    [compressed setLength: strm.total_out];
    return [NSData dataWithData:compressed];
}

The reading of the packets in the iOS client:

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    if(!readedData) readedData = [data mutableCopy];
    else [readedData appendData:data];
    // and some more code ...
}

When I've readed everything, I try to decode the data:

NSString *dataString = [[NSString alloc] initWithData:[NSData gzipDeflate:data] encoding:NSStringEncodingConversionAllowLossy];
NSLog(@"data string: %@", dataString);

I'm not sure what I'm doing wrong here. Could it be that Java and iOS are using other decoding protocols?

Monolo
  • 18,205
  • 17
  • 69
  • 103
Devos50
  • 2,025
  • 4
  • 25
  • 56
  • possible duplicate of [Java: Error creating a GZIPInputStream: Not in GZIP format](http://stackoverflow.com/questions/14466840/java-error-creating-a-gzipinputstream-not-in-gzip-format) – jarnbjo Apr 29 '13 at 11:44

2 Answers2

2

You decompress with inflate, not deflate.

Mark Adler
  • 101,978
  • 13
  • 118
  • 158
0

I was able to solve the issue posted above by using the gzip inflate as suggested by Mark. But make sure you use the correct gzip inflate written by the founder himself (https://stackoverflow.com/a/17822217/840384). then check the type of encoding used by you on the server side. there are some symbols like "/" which are not decoded properly which results in a invalid gzip stream. so take the encoded file and compare with the NSData Decoded File and make necessary changes.

Let me know if you need any other help with decoding at iOS end. I was able to achieve more than 60 % compression of the data exchanged over the network. Huge performance improvement from the iOS App perspective.

Community
  • 1
  • 1
devtorasia
  • 11
  • 6