我正在寻找一种方法,将字符串编码为尽可能短的长度并让它可解码(纯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编码/解码>解密>减压.这个输出尽可能接近你可能得到的,接近你可以得到的最短长度.
除了一切之外,我觉得有必要以这样一个事实作为结论:这只是理论上的事情,这是一个很好的挑战.肯定有更好的方法来实现你想要的结果 – 我将是第一个承认我的解决方案有点荒谬的人!