目前在我的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上获得相同的结果?
谢谢!
答案 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