1

I have built a string on an Android device, and I need to compress it and send it via Bluetooth Low Energy to a NodeJS application, where it needs to be unzipped.

On the Android/Java side, I compress it using GZIP and then Base64 encode it before sending it as follows:

public static String compress(String str) throws IOException {

    byte[] blockcopy = ByteBuffer
            .allocate(4)
            .order(java.nio.ByteOrder.LITTLE_ENDIAN)
            .putInt(str.length())
            .array();
    ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
    GZIPOutputStream gos = new GZIPOutputStream(os);
    gos.write(str.getBytes());
    gos.close();
    os.close();
    byte[] compressed = new byte[4 + os.toByteArray().length];
    System.arraycopy(blockcopy, 0, compressed, 0, 4);
    System.arraycopy(os.toByteArray(), 0, compressed, 4,
            os.toByteArray().length);
    return Base64.encodeToString(compressed, Base64.DEFAULT);
}

On the NodeJS side, I receive it, decode the Base64 and then attempt to unzip it as follows using the zlib library:

var buf = Buffer.from(raw, 'base64');
var data = zlib.gunzipSync(buf);

I have tested the Bluetooth communication on its own, and regular, unzipped data is collected completely fine. I have also tested sending the raw data uncompressed but encoded in Base64, and that also decodes and works fine. However, when attempting to decompress, I get the following error from the zlib library:

{ Error: incorrect header check
    at Gunzip.zlibOnError (zlib.js:153:15)
    at Gunzip._processChunk (zlib.js:411:30)
    at zlibBufferSync (zlib.js:144:38)
    at Object.gunzipSync (zlib.js:590:14)
    ...
    errno: -3, code: 'Z_DATA_ERROR' }

What am I doing wrong, and how can I go about fixing this?

Aman Singh
  • 934
  • 9
  • 16
  • Why do you base64 encode the bytes? That will increase the size by 33%. I assume you want to minimize the number of bytes sent since you're using gzip. – Emil Oct 07 '18 at 08:52
  • The BLE library always converts the bytes to a string, so I wanted to make sure there's no corruption from encoding. Base64 allows that @Emil – Aman Singh Oct 16 '18 at 23:41
  • Then you are using a bad BLE library since GATT values are by default binary in order to store any kind of value. Enforcing string seems simply incorrect. – Emil Oct 17 '18 at 01:09

2 Answers2

1

by adding those 4 bytes (blockcopy) to the compressed byte array, you are effectively modifying the the header of compressed data( what error says). either remove them like below:

public static String compress(String str) throws IOException {

    ByteArrayOutputStream os = new ByteArrayOutputStream(str.length());
    GZIPOutputStream gos = new GZIPOutputStream(os);
    gos.write(str.getBytes());
    gos.finish();
    gos.close();
    os.close();
    return Base64.encodeToString(os.toByteArray(), Base64.DEFAULT);
}

or omit the first 4 bytes( which is blockcopy) of var buf before you pass them to zlib.gunzipSync().

kamyar haqqani
  • 748
  • 6
  • 19
0

Try use the Apache Commons Codec Base64OutputStream, this will output a stream already in Base64. Check: How can I convert a string into a GZIP Base64 string?

Ohad Bitton
  • 460
  • 2
  • 14