具有解码可能性(缩短url)的最短可能编码字符串仅使用PHP

我正在寻找一种方法,将字符串编码为尽可能短的长度并让它可解码(纯PHP,没有SQL).我有工作脚本,但我对编码字符串的长度不满意.

场景:

链接到图像(取决于我想向用户显示的文件分辨率):

> www.mysite.com/share/index.php?img=/dir/dir/hi-res-img.jpg\u0026amp;w=700\u0026amp;h=500

编码链接(因此用户无法猜测如何获取更大的图像):

> www.mysite.com/share/encodedQUERYstring

所以,基本上我只想编码网址的搜索查询部分:

> img = / dir / dir / hi-res-img.jpg& w = 700& h = 500

我现在使用的方法将上面的查询字符串编码为:

> y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA

我使用的方法是:

 $raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';

 $encoded_query_string = base64_encode(gzdeflate($raw_query_string));
 $decoded_query_string = gzinflate(base64_decode($encoded_query_string)); 

如何缩短编码结果并仍然可以仅使用PHP对其进行解码?

解决方法:

我怀疑如果你不想让用户解码你需要更多考虑你的散列方法. base64的问题是base64字符串看起来像base64字符串.很有可能那些精通足以查看您的页面来源的人也可能会认出它.

第一部分:

a method that encodes an string to shortest possible length

如果您对URL词汇/字符的灵活性,这将是一个很好的起点.由于gzip使用后向引用可以获得很多收益,因此字符串太短没有什么意义.

考虑一下你的例子 – 你只在压缩中保存了2个字节,这些字节在base64填充中再次丢失:

非gzipped:string(52)“aW1nPS9kaXIvZGlyL2hpLXJlcy1pbWcuanBnJnc9NzAwJmg9NTAw”

Gzipped:string(52)“y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA ==”

如果你减少你的词汇大小,这自然会让你更好地压缩.假设我们删除了一些冗余信息

看看功能:

function compress($input, $ascii_offset = 38){
    $input = strtoupper($input);
    $output = '';
    //We can try for a 4:3 (8:6) compression (roughly), 24 bits for 4 chars
    foreach(str_split($input, 4) as $chunk) {
        $chunk = str_pad($chunk, 4, '=');

        $int_24 = 0;
        for($i=0; $i<4; $i++){
            //Shift the output to the left 6 bits
            $int_24 <<= 6;

            //Add the next 6 bits
            //Discard the leading ascii chars, i.e make
            $int_24 |= (ord($chunk[$i]) - $ascii_offset) & 0b111111;
        }

        //Here we take the 4 sets of 6 apart in 3 sets of 8
        for($i=0; $i<3; $i++) {
            $output = pack('C', $int_24) . $output;
            $int_24 >>= 8;
        }
    }

    return $output;
}

function decompress($input, $ascii_offset = 38) {

    $output = '';
    foreach(str_split($input, 3) as $chunk) {

        //Reassemble the 24 bit ints from 3 bytes
        $int_24 = 0;
        foreach(unpack('C*', $chunk) as $char) {
            $int_24 <<= 8;
            $int_24 |= $char & 0b11111111;
        }

        //Expand the 24 bits to 4 sets of 6, and take their character values
        for($i = 0; $i < 4; $i++) {
            $output = chr($ascii_offset + ($int_24 & 0b111111)) . $output;
            $int_24 >>= 6;
        }
    }

    //Make lowercase again and trim off the padding.
    return strtolower(rtrim($output, '='));
}

在那里发生的事情基本上是删除冗余信息,然后将4个字节压缩为3.这是通过有效地拥有ascii表的6位子集来实现的.移动此窗口,以便偏移量从有用的字符开始,并包括您当前使用的所有字符.

使用我使用的偏移量,您可以使用ASCII 38到102之间的任何内容.这将为您提供30字节的结果字符串,即9字节(24%)压缩!不幸的是,你需要使它具有URL安全性(可能使用base64),这可以使它恢复到40个字节.

我想在这一点上,你可以非常安全地假设你达到了阻止99.9%的人所需的“通过默默无闻的安全”水平.让我们继续,到你问题的第二部分

so the user can’t guess how to get the larger image

可以说上面已经解决了这个问题,但你需要做的是通过服务器上的秘密传递,最好是php openssl.以下代码显示了上述函数的完整使用流程和加密:

$method = 'AES-256-CBC';
$secret = base64_decode('tvFD4Vl6Pu2CmqdKYOhIkEQ8ZO4XA4D8CLowBpLSCvA=');
$iv = base64_decode('AVoIW0Zs2YY2zFm5fazLfg==');

$input = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
var_dump($input);

$compressed = compress($input);
var_dump($compressed);

$encrypted = openssl_encrypt($compressed, $method, $secret, false, $iv);
var_dump($encrypted);

$decrypted = openssl_decrypt($encrypted, $method, $secret, false, $iv);
var_dump($decrypted);

$decompressed = decompress($compressed);
var_dump($decompressed);

此脚本的输出如下:

string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"
string(30) "<��(��tJ��@�xH��G&(�%��%��xW"
string(44) "xozYGselci9i70cTdmpvWkrYvGN9AmA7djc5eOcFoAM="
string(30) "<��(��tJ��@�xH��G&(�%��%��xW"
string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"

你会看到整个周期:压缩>加密> base64编码/解码>解密>减压.这个输出尽可能接近你可能得到的,接近你可以得到的最短长度.

除了一切之外,我觉得有必要以这样一个事实作为结论:这只是理论上的事情,这是一个很好的挑战.肯定有更好的方法来实现你想要的结果 – 我将是第一个承认我的解决方案有点荒谬的人!

上一篇:如何使用Java逐步解码大的多字节字符串文件?


下一篇:有效的解决方案:在php中使用base32编码