【问题标题】:Different result from zlib deflation on iOS and Android. How to obtain same result?与 iOS 和 Android 上的 zlib 通缩不同的结果。如何获得相同的结果?
【发布时间】:2016-08-23 04:16:50
【问题描述】:

目前在我的iOS应用中我使用zlib对数据进行放气,我想在Android中实现相同的逻辑,以便在这两个平台上处理的放气数据兼容并可以传输。

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

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

在 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 上获得相同的结果?

谢谢!

【问题讨论】:

    标签: java android ios zlib deflate


    【解决方案1】:

    deflateInit2() 中的 16+MAX_WBITS 请求 gzip 格式,而 Deflater 类请求 zlib 格式。你可以去掉iOS代码中的16+来请求zlib格式。

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

    【讨论】:

      【解决方案2】:

      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
      

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 2023-03-15
          • 1970-01-01
          • 1970-01-01
          • 2021-07-17
          • 2015-12-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-16
          相关资源
          最近更新 更多