0

i am attempting to combine php-gzdeflate and pako. to compress the string i am using:

const compressed = ' <?php echo base64_encode(gzdeflate('Compress me')); ?> ' ;
// compressed now contains: c87PLShKLS5WyE0FAA==

but i cannot seem to read this string back using pako. i have tried the following:

var enc = new TextEncoder("utf-8");
pako.ungzip(enc.encode(compressed) );

i get this message back: uncaught incorrect header check

is there a simple way to compress using generic-php and inflate using pako?

so far i have tried adding various gzdeflate "levels" from one to nine, but none of them appear to make any difference. and at this point, i am just guessing.

and we would rather not install any special extension to php if possible

thank you very much.

edwardsmarkf
  • 1,233
  • 1
  • 13
  • 29

4 Answers4

3

Update to @edwardsmarkf's answer you can solve this without the atos function now. Most newer browsers have the TextDecoder api. You can use it like so:

const decoder = new TextDecoder();
const result = decoder.decode(pako.ungzip(atob(compressedBase64Data)));
2

I couldn't get the answers here to work, so I did some research.

As PleaseStand pointed out here, the problem is that PHP uses UTF-8 strings, while JS uses UTF-16 strings. Hence, the binary string to base64 string encoding will differ.

The solution I used is to force JS to interpret the data as UTF-8. This is straightforward, as pako accepts and returns Uint8Arrays, which are essentially UTF-8 strings.:

Compress in JS, Decompress in PHP:

//JS
const pako = require('pako');
const compress = str => Buffer.from(pako.deflateRaw(str)).toString('base64');
console.log(compress('asdfasdfasdfasdf')); //SyxOSUtEwgA=
//PHP
function decompress($str) { return gzinflate(base64_decode($str)); }
echo decompress('SyxOSUtEwgA='); //asdfasdfasdfasdf

Compress in PHP, Decompress in JS:

//PHP
function compress($str) { return base64_encode(gzdeflate($str, 9)); }
echo compress('asdfasdfasdf'); //SyxOSUuEYgA=
//JS
const pako = require('pako');
const decompress = str => pako.inflateRaw(Buffer.from(str, 'base64'), {to: 'string'});
console.log(decompress('SyxOSUuEYgA=')); //asdfasdfasdfs

Note: Buffer instances are also Uint8Array instances, hence we don't need to convert the Buffer to a Uint8Array before giving it to pako.

These functions are also compatible within the languages:

//JS
console.log(decompress(compress('asdfasdfasdfasdf'))); //asdfasdfasdfasdf
//PHP
echo decompress(compress('asdfasdfasdfasdf')); //asdfasdfasdfasdf

For JS, this works out of the box in NodeJs. In a browser environment, you will need a polyfil for Buffer.

For PHP, remember to install the Zlib extension.

PStigerID
  • 61
  • 1
  • 8
0

This appears to work(below)

Steps involved:

server side (php):

1) gzdeflate using ZLIB_ENCODING_DEFLATE option

2) base64_encode

client side:(jScript)

1) atob

2) pako.ungzip

3) atos function

<script src='//cdnjs.cloudflare.com/ajax/libs/pako/1.0.5/pako_deflate.js' type='text/javascript'></script>

<script type='text/javascript'>
const compressedDEFLATE = '<?php echo base64_encode(gzdeflate('Compress me', 6, ZLIB_ENCODING_DEFLATE )); ?>'  ;

function atos(arr) {
    for (var i=0, l=arr.length, s='', c; c = arr[i++];)
        s += String.fromCharCode(
            c > 0xdf && c < 0xf0 && i < l-1
                ? (c & 0xf) << 12 | (arr[i++] & 0x3f) << 6 | arr[i++] & 0x3f
            : c > 0x7f && i < l
                ? (c & 0x1f) << 6 | arr[i++] & 0x3f
            : c
        );
    return s
}
    alert ( atos(pako.ungzip( atob(compressedDEFLATE)  ) )  );
</script>
edwardsmarkf
  • 1,233
  • 1
  • 13
  • 29
0

I'm not familiar with php, so I kinda struggled with this problem, so I thought to post a minimal working solution in php:

$response = gzdeflate('My data', 9, ZLIB_ENCODING_DEFLATE);

header('Content-Encoding: deflate');
echo $response;

No need to use pako after this, the data will be decompressed by the browser.

Example if you're requesting json formatted data:

$.ajax({
    type: 'GET',
    url: 'http://target.com',
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    headers : {'Accept-Encoding': 'deflate '},
})
.done(function(res) {
    console.log(res)
})
.fail(function(xhr, textStatus, errorThrown) {
});
olinox14
  • 5,652
  • 2
  • 19
  • 36