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,387
  • 2
  • 16
  • 31

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)));
3

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
  • 81
  • 1
  • 8
1

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
  • 6,177
  • 2
  • 22
  • 39
  • This is the best answer. No need to reinvent the wheel or use pako at all if you are sending data to the client. If you are sending data from the client see php demo here: https://github.com/nodeca/pako/issues/187#issuecomment-1246931993 – Tom Davenport Sep 14 '22 at 16:03
  • BTW, you don't need to set the Accept-Encoding header in the call ( this is actually illegal for xhr and the browser will ignore it or throw an error). Only thing you need to do is set the appropriate Content-Encoding header in the php response. – Tom Davenport Sep 15 '22 at 07:25
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,387
  • 2
  • 16
  • 31