六种PHP图片上传重命名方案研究与总结

来源:互联网 发布:彩虹秒赞 v7源码 编辑:程序博客网 时间:2024/04/30 13:00

一、适用场景

无法使用从数据库中返回的自增长数字,给上传图片重命名。

这是图片或文件上传的流程决定的。一般图片上传处理过程是,先上传图片到服务器,重命名之后,插入到数据库。

 

也就是说,在数据库中非常容易获得的自增长id,无法用于给上传的图片重命名,来避免文件名称的重复,而采用从数据库中获取最大id加1的方式,增加了数据库连接的次数,不适用于高并发和数据量巨大的情况。

 

二、常规方案

1、guid:32 字符十六进制数

格式:GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中每个 x 是 0-9 或 a-f 范围内的一个32位十六进制数。例如:6F9619FF-8B86-D011-B42D-00C04FC964FF 即为有效的 GUID 值。

优点:几乎不会重复;

缺点:对于给上传的图片重命名,还是过长了。

用法:

Php代码  收藏代码
  1. /* 
  2.     com_create_guid()是php5版本支持的功能,对于不支持的版本,可以自己进行定义 
  3. */  
  4. function guid(){  
  5.    if (function_exists('com_create_guid')){  
  6.        return com_create_guid();  
  7.    }else{  
  8.        mt_srand((double)microtime()*10000);//optional for php 4.2.0 and up.  
  9.        echo(mt_rand());  
  10.        $charid = strtoupper(md5(uniqid(rand(), true)));  
  11.        $hyphen = chr(45);// "-"  
  12.        $uuid = chr(123)// "{"  
  13.                .substr($charid, 0, 8).$hyphen  
  14.                .substr($charid, 8, 4).$hyphen  
  15.                .substr($charid,12, 4).$hyphen  
  16.                .substr($charid,16, 4).$hyphen  
  17.                .substr($charid,20,12)  
  18.                .chr(125);// "}"  
  19.        return $uuid;  
  20.    }  
  21. }  

 

2、MD5

与guid一样会输出32字符十六进制数,区别是guid是随机产生的,md5需要根据输入的数据生成。

例子:

Php代码  收藏代码
  1. <?php  
  2. $str = "Hello";  
  3. echo md5($str);  
  4. ?>  

 

输出:

8b1a9953c4611296a827abf8c47804d7

优点:可以根据输入的种子数据来控制输出的数值,如果种子数据是规律性不重复的,通过md5可以对数据进行保护,产生很大的混淆作用;

缺点:32位字符过长;需提供不重复的种子数据;

用法:高并发,以秒为种子数据,仍然会出现重复现象。

Php代码  收藏代码
  1. <?php  
  2. /* 
  3. *结合time()函数使用,以1970年到当前时间的秒数作为种子数 
  4. */  
  5. $str=time();  
  6. echo md5($str);  
  7. ?>  

 

3、uniqid():返回13或23位字符串

对于我们目的来说,uniqid()像是md5()的改进版,尤其是我们可以采用差异性标识作为字符串前缀,可以降低重复命名出现的几率。

对于非高并发等极端情况,推荐使用此函数,已经可以满足一般性需求。

详细说明:

定义:uniqid() 函数基于以微秒计的当前时间,生成一个唯一的 ID;

用法:uniqid(prefix,more_entropy);

说明:prefix可以为输出的字符串添加前缀,示例如下,more_entropy参数为true时,将输出23位字符串。

Php代码  收藏代码
  1. <?php  
  2. var_dump(uniqid());  
  3. var_dump(uniqid("a"));  
  4. ?>  

 

输出结果为:

Php代码  收藏代码
  1. string(13) "51734aa562254" string(14) "a51734aa562257"  
 

优点:13位字符串长度,是可以接受的文件命名长度;可以添加前缀,结果包含数据混淆,能够避免反推原始数据;

缺点:同md5相似,高并发,以秒为种子数据,仍然会出现重复现象。

 

三、升级版方案

1、fast_uuid:返回17位数字

有点像uniqid()的不完全定制版,这个函数里面出现的“种子数开始时间”概念很有启发性。

time()和uniqid()中默认用到的时间都是从1970年开始计算的,长度有十位(1366512439),采用“种子数开始时间”能够缩小这个数值,因为我们实际上需要的,仅仅是一个能够自动增长的数值即可。

起始时间自定义以后,除了减少长度,还能够起到混淆的作用。

Php代码  收藏代码
  1. /* 
  2. * 参数 suffix_len指定 生成的 ID 值附加多少位随机数,默认值为 3 
  3. * @param int suffix_len 
  4. * @return string 
  5. */  
  6. function fast_uuid($suffix_len=3){  
  7.     //! 计算种子数的开始时间  
  8.     $being_timestamp = strtotime('2013-3-21');  
  9.   
  10.     $time = explode(' ', microtime());  
  11.     $id = ($time[1] - $being_timestamp) . sprintf('%06u'substr($time[0], 2, 6));  
  12.     if ($suffix_len > 0)  
  13.     {  
  14.         $id .= substr(sprintf('%010u', mt_rand()), 0, $suffix_len);  
  15.     }  
  16.     return $id;  
  17. }  
 

 

输出:

29832412631099013

 

2、time()+随机数

上例中已经出现了随机数的使用,是为了解决一秒下发生的多次请求。提供两个函数如下:

Php代码  收藏代码
  1. <?php  
  2. function random($length) {  
  3.     $hash = '';  
  4.     $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';  
  5.     $max = strlen($chars) - 1;  
  6.     PHP_VERSION < '4.2.0' && mt_srand((double)microtime() * 1000000);  
  7.     for($i = 0; $i < $length$i++) {  
  8.         $hash .= $chars[mt_rand(0, $max)];  
  9.     }  
  10.     return $hash;  
  11. }  
  12. function random2($length$numeric = 0) {  
  13.     PHP_VERSION < '4.2.0' ? mt_srand((double)microtime() * 1000000) : mt_srand();  
  14.     $seed = base_convert(md5(print_r($_SERVER, 1).microtime()), 16, $numeric ? 10 : 35);  
  15.     $seed = $numeric ? (str_replace('0'''$seed).'012340567890') : ($seed.'zZ'.strtoupper($seed));  
  16.     $hash = '';  
  17.     $max = strlen($seed) - 1;  
  18.     for($i = 0; $i < $length$i++) {  
  19.         $hash .= $seed[mt_rand(0, $max)];  
  20.     }  
  21.     return $hash;  
  22. }  
  23. ?>  

 

四、最终方案

思路:userid+秒+随机数。其中“userid+秒”10进制转64进制,缩减位数。

说明:

1、userid: 64进制最大值“ZZZZ”转换为十进制等于”16777215“,”ZZZ“转换为十进制最大值等于”262143“;

2、秒:设置自己的时间起点。

$less=time()-strtotime(’2012-4-21′) 转换为64进制”1SpRe“,5位

$less=time()-strtotime(’2013-3-21′) 转换为64进制”_jHY“,4位

3、随机数:使用random(3)生成3位随机数。

最终结果:4位userid+4位秒+3位随机数=11位字符串。虽然与uniqid()结果看上去相似,但是强壮度有所提高。

 

五、十进制转64进制算法

1、算法1

Php代码  收藏代码
  1. const KeyCode = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';  
  2.   
  3.     /** 
  4.      * 将64进制的数字字符串转为10进制的数字字符串 
  5.      * @param $m string 64进制的数字字符串 
  6.      * @param $len integer 返回字符串长度,如果长度不够用0填充,0为不填充 
  7.      * @return string 
  8.      */  
  9.     function hex64to10($m$len = 0) {  
  10.         $m = (string)$m;  
  11.         $hex2 = '';  
  12.         $Code = KeyCode;  
  13.         for($i = 0, $l = strlen($Code); $i < $l$i++) {  
  14.             $KeyCode[] = $Code[$i];  
  15.         }  
  16.         $KeyCode = array_flip($KeyCode);  
  17.   
  18.         for($i = 0, $l = strlen($m); $i < $l$i++) {  
  19.             $one = $m[$i];  
  20.             $hex2 .= str_pad(decbin($KeyCode[$one]), 6, '0', STR_PAD_LEFT);  
  21.         }  
  22.         $return = bindec($hex2);  
  23.   
  24.         if($len) {  
  25.             $clen = strlen($return);  
  26.             if($clen >= $len) {  
  27.                 return $return;  
  28.             }  
  29.             else {  
  30.                 return str_pad($return$len'0', STR_PAD_LEFT);  
  31.             }  
  32.         }  
  33.         return $return;  
  34.     }  
  35.   
  36.     /** 
  37.      * 将10进制的数字字符串转为64进制的数字字符串 
  38.      * @param $m string 10进制的数字字符串 
  39.      * @param $len integer 返回字符串长度,如果长度不够用0填充,0为不填充 
  40.      * @return string 
  41.      */  
  42.     function hex10to64($m$len = 0) {  
  43.         $KeyCode = KeyCode;  
  44.         $hex2 = decbin($m);  
  45.         $hex2 = str_rsplit($hex2, 6);  
  46.         $hex64 = array();  
  47.         foreach($hex2 as $one) {  
  48.             $t = bindec($one);  
  49.             $hex64[] = $KeyCode[$t];  
  50.         }  
  51.         $return = preg_replace('/^0*/''', implode(''$hex64));  
  52.         if($len) {  
  53.             $clen = strlen($return);  
  54.             if($clen >= $len) {  
  55.                 return $return;  
  56.             }  
  57.             else {  
  58.                 return str_pad($return$len'0', STR_PAD_LEFT);  
  59.             }  
  60.         }  
  61.         return $return;  
  62.     }  
  63.   
  64.     /** 
  65.      * 将16进制的数字字符串转为64进制的数字字符串 
  66.      * @param $m string 16进制的数字字符串 
  67.      * @param $len integer 返回字符串长度,如果长度不够用0填充,0为不填充 
  68.      * @return string 
  69.      */  
  70.     function hex16to64($m$len = 0) {  
  71.         $KeyCode = KeyCode;  
  72.         $hex2 = array();  
  73.         for($i = 0, $j = strlen($m); $i < $j; ++$i) {  
  74.             $hex2[] = str_pad(base_convert($m[$i], 16, 2), 4, '0', STR_PAD_LEFT);  
  75.         }  
  76.         $hex2 = implode(''$hex2);  
  77.         $hex2 = str_rsplit($hex2, 6);  
  78.         foreach($hex2 as $one) {  
  79.             $hex64[] = $KeyCode[bindec($one)];  
  80.         }  
  81.         $return = preg_replace('/^0*/''', implode(''$hex64));  
  82.         if($len) {  
  83.             $clen = strlen($return);  
  84.             if($clen >= $len) {  
  85.                 return $return;  
  86.             }  
  87.             else {  
  88.                 return str_pad($return$len'0', STR_PAD_LEFT);  
  89.             }  
  90.         }  
  91.         return $return;  
  92.     }  
  93.   
  94.     /** 
  95.      * 功能和PHP原生函数str_split接近,只是从尾部开始计数切割 
  96.      * @param $str string 需要切割的字符串 
  97.      * @param $len integer 每段字符串的长度 
  98.      * @return array 
  99.      */  
  100.     function str_rsplit($str$len = 1) {  
  101.         if($str == null || $str == false || $str == ''return false;  
  102.         $strlen = strlen($str);  
  103.         if($strlen <= $lenreturn array($str);  
  104.         $headlen = $strlen % $len;  
  105.         if($headlen == 0) {  
  106.             return str_split($str$len);  
  107.         }  
  108.         $return = array(substr($str, 0, $headlen));  
  109.         return array_merge($returnstr_split(substr($str$headlen), $len));  
  110.     }  
  111.   
  112. $a=idate("U");  
  113. echo "\r\n<br />e:" . hex10to64($a);  
  114. echo "\r\n<br />e:" . hex64to10(hex10to64($a));  

 

2、算法2

Php代码  收藏代码
  1. function dec2s4($dec) {    
  2.     $base = '0123456789_$abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';    
  3.     $result = '';    
  4.   
  5.     do {    
  6.         $result = $base[$dec % 64] . $result;    
  7.         $dec = intval($dec / 64);    
  8.     } while ($dec != 0);    
  9.   
  10.     return $result;    
  11. }    
  12.   
  13. function  s42dec($sixty_four) {    
  14.     $base_map = array ( '0' => 0,'1' => 1,'2' => 2,'3' => 3,'4' => 4,'5' => 5,'6' => 6,'7' => 7,'8' => 8,'9' => 9,'_' => 10,'$' => 11,'a' => 12,'b' => 13,'c' => 14,'d' => 15,'e' => 16,'f' => 17,'g' => 18,'h' => 19,'i' => 20,'j' => 21,'k' => 22,'l' => 23,'m' => 24,'n' => 25,'o' => 26,'p' => 27,'q' => 28,'r' => 29,'s' => 30,'t' => 31,'u' => 32,'v' => 33,'w' => 34,'x' => 35,'y' => 36,'z' => 37,'A' => 38,'B' => 39,'C' => 40,'D' => 41,'E' => 42,'F' => 43,'G' => 44,'H' => 45,'I' => 46,'J' => 47,'K' => 48,'L' => 49,'M' => 50,'N' => 51,'O' => 52,'P' => 53,'Q' => 54,'R' => 55,'S' => 56,'T' => 57,'U' => 58,'V' => 59,'W' => 60,'X' => 61,'Y' => 62,'Z' => 63);     $result = 0;    
  15.     $len = strlen($sixty_four);    
  16.   
  17.     for ($n = 0; $n < $len$n++) {    
  18.         $result *= 64;    
  19.         $result += $base_map[$sixty_four{$n}];    
  20.     }    
  21.   
  22.     return $result;    
  23. }    
  24.   
  25. $a=idate("U");  
  26. var_dump(dec2s4($a));    
  27. var_dump(s42dec(dec2s4($a)));  

 

3、算法效率测试

Php代码  收藏代码
  1. $strarr = array();  
  2. $time1 = microtime(true);  
  3. for($i = 0; $i < 10000; ++$i) {  
  4.      $str = idate("U")+$i;  
  5.      $strarr[] = "{$i}->$str\r\n<br>";  
  6.  }  
  7.  $time2 = microtime(true);  
  8.  $time3 = $time2 - $time1;  
  9.   
  10.  $time1 = microtime(true);  
  11.  for($i = 0; $i < 10000; ++$i) {  
  12.      $str = dec2s4(idate("U")+$i);  
  13.     $strarr[] = "{$i}->$str\r\n<br>";  
  14. }  
  15. $time2 = microtime(true);  
  16. echo "\r\n<br />运行10000次用时(秒):" . ($time2 - $time1 - $time3);  

 

4、测试结果

算法1:0.1687250137329

算法2:0.044965028762817

 

5、结论

算法1虽然效率上差一些,但是可以把md5生成的16进制转化为64进制,能够使用在必须使用md5的环境下缩短字符串。

 

六、总结

本文涉及了上传图片重命名可以能使用的几种方法,其中关键点是使用10进制转换为64进制来进行字符串的缩减。

例如,使用fast_uuid生成的17位数字,转换为64进制仅有7位字符;

具体使用,可以根据自身情况灵活使用,希望对大家有所帮助。

转载请注明:程序猿 » 六种PHP图片上传重命名方案研究与总结

0 0