Почему результат алгоритма GZip не совпадает в Android и .Net? - PullRequest
5 голосов
/ 14 августа 2011

Почему результат алгоритма GZip не совпадает в Android и .Net?

Мой код в Android:

    public static String compressString(String str) {

    String str1 = null;
    ByteArrayOutputStream bos = null;
    try {
        bos = new ByteArrayOutputStream();
        BufferedOutputStream dest = null;

        byte b[] = str.getBytes();
        GZIPOutputStream gz = new GZIPOutputStream(bos, b.length);
        gz.write(b, 0, b.length);
        bos.close();
        gz.close();

    } catch (Exception e) {
        System.out.println(e);
        e.printStackTrace();
    }
    byte b1[] = bos.toByteArray();
    return Base64.encode(b1);
}

Мой код в .Net WebService:

    public static string compressString(string text)
{
    byte[] buffer = Encoding.UTF8.GetBytes(text);
    MemoryStream ms = new MemoryStream();
    using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
    {
        zip.Write(buffer, 0, buffer.Length);
    }

    ms.Position = 0;
    MemoryStream outStream = new MemoryStream();

    byte[] compressed = new byte[ms.Length];
    ms.Read(compressed, 0, compressed.Length);

    byte[] gzBuffer = new byte[compressed.Length + 4];
    System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
    System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
    return Convert.ToBase64String(gzBuffer);
}

В Android:

compressString("hello"); -> "H4sIAAAAAAAAAMtIzcnJBwCGphA2BQAAAA=="

В .Net:

compressString("hello"); -> "BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA=="

Интересно, что когда я использую метод Decompress в Android для распаковки результата метода .Net compressString , он возвращает исходную строку правильно, но я получаю ошибку, когда распаковываю результат Android сжатый метод String .

Android-метод распаковки:

    public static String Decompress(String zipText) throws IOException {
    int size = 0;
    byte[] gzipBuff = Base64.decode(zipText);

    ByteArrayInputStream memstream = new ByteArrayInputStream(gzipBuff, 4,
            gzipBuff.length - 4);
    GZIPInputStream gzin = new GZIPInputStream(memstream);

    final int buffSize = 8192;
    byte[] tempBuffer = new byte[buffSize];
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    while ((size = gzin.read(tempBuffer, 0, buffSize)) != -1) {
        baos.write(tempBuffer, 0, size);
    }
    byte[] buffer = baos.toByteArray();
    baos.close();

    return new String(buffer, "UTF-8");
}

Я думаю, что в методе Android compressString есть ошибка. Кто-нибудь может мне помочь?

Ответы [ 3 ]

2 голосов
/ 13 мая 2012

Согласно этому ответу , у меня 4 метода.Android и .net сжимают и распаковывают методы.Эти методы совместимы друг с другом, за исключением одного случая.

2 голосов
/ 14 августа 2011

В версии для Android вы должны закрыть bos после закрыть gz.

Кроме того, эта строка в compressString может вызвать проблемы:

byte b[] = str.getBytes();

Это преобразует символы в байты с использованием кодировки по умолчанию на устройстве, которая почти наверняка не является UTF-8.Версия .NET, с другой стороны, использует UTF8.В Android попробуйте вместо этого:

byte b[] = str.getBytes("UTF-8");

РЕДАКТИРОВАТЬ: При дальнейшем рассмотрении вашего кода, я предлагаю вам переписать его так:

byte b[] = str.getBytes("UTF-8");
GZIPOutputStream gz = new GZIPOutputStream(bos);
gz.write(b, 0, b.length);
gz.finish();
gz.close();
bos.close();

Изменения: используйте UTF8 для кодирования символов;используйте размер внутреннего буфера по умолчанию для GZIPOutputStream;позвоните gz.close() перед вызовом bos.close() (последний, вероятно, даже не нужен);и позвоните gz.finish(), прежде чем позвонить gz.close().

РЕДАКТИРОВАТЬ 2:

Хорошо, я должен был понять, прежде чем что-то происходит.Класс GZIPOutputStream, на мой взгляд, глупый дизайн.Он не может определить требуемое сжатие, и сжатие по умолчанию установлено равным none.Вам нужно создать его подкласс и переопределить сжатие по умолчанию.Самый простой способ сделать это:

GZIPOutputStream gz = new GZIPOutputStream(bos) {
    {
        def.setLevel(Deflater.BEST_COMPRESSION);
    }
};

Это сбросит внутренний дефлятор, который GZIP использует для обеспечения наилучшего сжатия.(Кстати, в случае, если вы не знакомы с ним, синтаксис, который я здесь использую, называется блок инициализатора экземпляра .)

0 голосов
/ 14 августа 2011

Основное отличие состоит в том, что ваш код .NET помещает длину сжатых данных в первые четыре байта двоичных данных.Ваши Java-коды этого не делают.В нем отсутствует поле длины.

Когда вы распаковываете его, вы ожидаете длину в первых четырех байтах и ​​начинаете декомпрессию GZIP в позиции 4 (пропуская первые четыре байта).

...