我正在寻找一种方法,将字符串编码到尽可能短的长度,并使其可解码(纯PHP,无SQL)。我有工作脚本,但对编码字符串的长度不满意。
脚本:
链接到图像(取决于我要向用户显示的文件分辨率):
www.mysite.com/share/index.php?img=/dir/dir/hi-res img.jpg&w=700&h=500
编码链接(因此用户无法猜测如何获得更大的图像):
www.mysite.com/share/encodedquerystring
因此,basicaly我只想对URL的搜索查询部分进行编码:
img=/dir/dir/hi-res img.jpg&w=700&h=500
我现在使用的方法将上述查询字符串编码为:
Y8xNT9vpyswc44xm3润滑油3m3hs9rij0txjbcwmdtqxbuwmdaa
我使用的方法是:$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字符串。有一个很好的机会,一个足够聪明的人看你的网页来源可能也会认识到它。
第一部分:
将字符串编码为尽可能短的长度的方法。
如果你对你的网址词汇/字符很灵活,这将是一个很好的开始。由于gzip使用返回引用获得了很大的收益,所以由于字符串太短,所以没有什么意义。
考虑您的示例-您在压缩中只保存了2个字节,这些字节在base64 padding中再次丢失:
非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%的人所需要的“通过默默无闻的安全”水平,你是相当安全的。不过,让我们继续,到问题的第二部分
所以用户无法猜测如何获得更大的图像
可以说,上面已经解决了这个问题,但是您需要做的是通过服务器上的一个秘密来传递这个问题,最好使用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) "
string(44) "xozYGselci9i70cTdmpvWkrYvGN9AmA7djc5eOcFoAM="
string(30) "
string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"
您将看到整个循环:压缩>加密>base64编码/解码>解密>解压。它的输出将尽可能接近你真正得到的长度,接近你能得到的最短长度。
除此之外,我觉得有必要得出这样的结论:它只是理论上的,这是一个值得思考的挑战。当然有更好的方法来达到你想要的结果-我会第一个承认我的解决方案有点荒谬!