iOS和Android上zlib通缩的结果不同。如何获得相同的结果?

时间:2016-08-23 04:16:50

标签: java android ios zlib deflate

目前在我的iOS应用程序中,我使用zlib来缩小数据,我想在Android中实现相同的逻辑,以便在这两个平台中处理的缩减数据是兼容的并且可以传输。

在下面的代码中,inputString都是随机字符串,如:

  

开发人员信任Stack Overflow以帮助解决编码问题并使用Stack Overflow Careers找到工作机会。我们致力于让互联网变得更加美好,我们的产品旨在丰富开发人员在职业生涯中成长和成熟的生活。

在iOS中,使用以下代码段:

NSData *rawData = [inputString dataUsingEncoding:NSUTF8StringEncoding];
NSInputStream * src = [NSInputStream inputStreamWithData:rawData];
[src open];
NSOutputStream * dest = [NSOutputStream outputStreamToMemory];
[dest open];
int res = [self deflateDataStream:src toOutputStream:dest level:Z_DEFAULT_COMPRESSION];
[dest close];
[src close];

if (res != Z_OK) return nil;

NSData *ret = [dest propertyForKey:NSStreamDataWrittenToMemoryStreamKey];

+ (int) deflateDataStream:(NSInputStream *)source toOutputStream:(NSOutputStream *)dest level:(int)level {
    int ret, flush;
    unsigned have;
    z_stream strm;
    unsigned char inBuf[CHUNK];
    unsigned char outBuf[CHUNK];

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    ret = deflateInit2(&strm, level, Z_DEFLATED, (16+MAX_WBITS), MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
    if (ret != Z_OK) return ret;

    do {
        NSInteger res = [source read:inBuf maxLength:CHUNK];
        if (res < 0)  {
            NSLog(@"!!! Error reading stream: %ld %@", (long)source.streamStatus, source.streamError);
            (void)deflateEnd(&strm);
            return Z_ERRNO;
        }

        flush = [source hasBytesAvailable] ? Z_NO_FLUSH : Z_FINISH;
        strm.avail_in = (uInt)res;
        strm.next_in = inBuf;

        do {
            strm.avail_out = CHUNK;
            strm.next_out = outBuf;
            ret = deflate(&strm, flush);
            assert(ret != Z_STREAM_ERROR);
            have = CHUNK - strm.avail_out;

            res = [dest write:outBuf maxLength:have];
            if (res != have || res < 0) {
                (void)deflateEnd(&strm);
                return Z_ERRNO;
            }
        } while (strm.avail_out == 0);
        assert(strm.avail_in == 0);
    } while (flush != Z_FINISH);
    assert(ret == Z_STREAM_END);

    (void)deflateEnd(&strm);
    return Z_OK;
}

之后压缩数据将被进一步处理(经过加密等)并保存。

然后对于我目前正在处理的Android版本,从文档页面here Deflater类使用zlib逻辑执行通缩,所以我尝试使用以下代码段:

byte[] dataToBeDeflated = inputString.getBytes(Charset.forName("UTF-8"));

Deflater deflater = null;
ByteArrayOutputStream outputStream = null;
byte[] deflatedData = null;

try {
    deflater = new Deflater();
    deflater.setStrategy(Deflater.DEFAULT_STRATEGY);
    deflater.setLevel(Deflater.DEFAULT_COMPRESSION);
    deflater.setInput(dataToBeDeflated);
    outputStream = new ByteArrayOutputStream(dataToBeDeflated.length);
    deflater.finish();
    byte[] buffer = new byte[1024];
    while (!deflater.finished()) {
        int count = deflater.deflate(buffer);
        outputStream.write(buffer, 0, count);
    }

    deflatedData = outputStream.toByteArray();
} catch (Exception e) {
    Log.e(TAG, "Deflate exception", e);
} finally {
    if (outputStream != null) {
        try {
            outputStream.close();
        } catch (IOException e) {
            Log.e(TAG, "Failed to close the output stream", e);
        }
    }
}

然而,Android上面的实现返回的结果并没有产生与iOS相同的结果,使我现有的iOS应用程序无法使用它。

使用我引用的测试字符串,iOS产生大小为197字节的NSData,其中原始字符串数据为273字节。虽然Android上的原始输入大小也是273字节,但上面的实现给出了大小为185的结果。

改变iOS方面的逻辑目前是不可行的,因为这将涉及许多额外的过程,如提交审查等。

我认为两个平台的基础算法应该相同?如果是这种情况,为什么结果会有所不同?我做错了什么?我如何纠正它并在Android上获得相同的结果?

谢谢!

3 个答案:

答案 0 :(得分:3)

16+MAX_WBITS中的deflateInit2()正在请求gzip格式,而Deflater类正在请求zlib格式。您可以删除iOS代码中的16+以请求zlib格式。

请注意,输出可能仍然不同,因为不要求来自不同压缩器的压缩数据对于相同的输入是相同的。重要的是,你从解压缩器得到的东西与你给的压缩器相同。

答案 1 :(得分:0)

你正在使用MAX_MEM_LEVEL(9)在iOS上使用不同级别,而在Android上使用DEFAULT_COMPRESSION( - 1)。尝试在Android上使用BEST_COMPRESSION(9)。

答案 2 :(得分:0)

iOS中有两种deflate方法:

1. deflateInit(strm, level)
2. deflateInit2(strm, level, method, windowBits, memLevel, strategy)

第一个与java的deflater兼容。还要确保在iOS和Java(Android)中使用相同的压缩级别。

压缩级别:

   -1: default
    0: NO_COMPRESSION
    1: BEST_SPEED //generally used 
       ......
    9: BEST_COMPRESSION