在我们的项目中,图片是根据模板的不同显示不同的规格,所以无法预先生成缩略图。而图片是存储在专门的图片文件服务器上的,我们要求图片请求除第一次生成之外,访问的是真实的图片文件。主要思路是通过404跳转生成缩略图。具体方法:
1. 图片上传到文件服务器,图片的相对路径保存到相应的数据字段中。
2.模板显示的时候,根据模板要求,生成该图片缩略图的URL,缩略图URL中包含有图片原始地址,缩略图大小,缩略规则,水印规则
/**
* 根据原图名获取缩略图的访问地址,规格段需要进行加密
*
* @param String $filename 文件名
* @param int $width 宽度
* @param int $height 高度
* @param Char $tyep f:fix,fw:fix width,fh:fix height,c:cut,a:auto,n:原图,不做任何处理,图片生成方式
* @param $watermark 水印规则,0表示不加水印
* @param String $toJpeg 是否转换为jpg y:转换 n:不转换
* @return String 缩略图访问地址
*/
public static function getImgUrl($filename,$width=0,$height=0,$type='f',$watermark=0,$toJpeg='n')
{
//如果是远程路径,直接返回
if(strpos($filename,"http://") === 0){
return $filename;
}
$base_img_url = IMG_BASE_URL;
if($filename == ""){
return DEFAULT_IMG_URL;
}
if($type == 'n'){
return $base_img_url.$filename;
}
$extend = pathinfo($filename);
$ext = strtolower($extend["extension"]);
//是否转换为jpg
if(($ext == 'png') && ($toJpeg == 'y')){
$format = 'jpg';
}else{
$format = $ext;
}
//$url = str_replace("images",$base_img_url,$filename);
$url = $base_img_url.$filename;
$width = intval($width);
$height = intval($height);
if($watermark===0){
return str_replace(".$ext",'_'.$ext.'_cacheimages'.'_H_'.Util::encrypt_unrand($width.'_'.$height."_$type").".".$format,$url);
}else{
return str_replace(".$ext",'_'.$ext.'_cacheimages'.'_H_'.Util::encrypt_unrand($width.'_'.$height."_$type"."_".$watermark).".".$format,$url);
}
}
3.浏览器向图片服务器请求缩略图的URL,该URL 对应的图片如果已经生成则正常访问
4.如果该图片不存在,则触发404跳转,跳转到我们设定的缩略图生成文件。这一步需要在web服务中设置,将默认的404文件 改成我们的缩略图生成文件,我的是Apache,
<VirtualHost *>
serverName img.xxx.com
Header set Access-Control-Allow-Origin http://www.xxx.com
DocumentRoot D:/www/xxx/trunk/static/
<Directory "D:/www/xxx/trunk/static/">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
Allow from all
</Directory>
<span style="background-color: rgb(255, 255, 153);">ErrorDocument 404 /imageServer/404.php</span>
</VirtualHost>
5.缩略图生成文件时,先解析URL,得出缩略图的原图和各种规则,然后生成,并保存到URL所指定的地址,然后直接输出缩略图数据,显示在浏览器中。
/**
* 404错误页面处理图片缩略图生成
*/
include "config.php";
$imgpath = $_SERVER['REDIRECT_URL'] ? $_SERVER['REDIRECT_URL'] : $_SERVER['REQUEST_URI'];
$imgpath = substr($imgpath,1,strlen($imgpath)-1);
$pos = strpos($imgpath,'?');
if($pos !== false){
$imgpath = substr($imgpath,0,$pos);
}
$imgarr = pathinfo($imgpath);
if(!in_array(trim(strtolower($imgarr['extension'])),array('jpg','jpeg','gif','png','bmp'))){
exit;
}
$filename = $imgarr['filename'];
$arr = explode("_",$filename);
$arr_len = count($arr);
/** 加密后,解密 **/
$spec = explode("_",decrypt2($arr[$arr_len-1]));
$w = $spec[0];
$h = $spec[1];
if($arr[$arr_len-2] != 'H'){
$w = ceil($w/2);
$h = ceil($h/2);
}
$spec['2'] = trim($spec['2']);
$type = in_array($spec['2'],array('a','f','fw','fh','c','tc','cc')) ? $spec['2'] : 'a';
$watermark = isset($spec['3']) ? $spec['3'] : 0;
include "imageResize.func.php";
//获取原图路径
$arr1 = explode("_cacheimages_",$filename);
$src_name = $arr1[0];
if($arr_len == 6){
$file = IMG_DIR.$imgarr['dirname'].'/'.$arr[0].'_'.$arr[1].'.'.$arr[$arr_len-4];
}else{
$arr2 = explode("_",$src_name);
$arr2_len = count($arr2);
if($arr2_len == 1){
$file = IMG_DIR.$imgarr['dirname'].'/'.$arr2[0].".".$imgarr['extension'];
}else{
$ex = strtolower ($arr2[$arr2_len-1]);
if($ex == 'png' || $ex == 'jpg' || $ex == 'gif' || $ex == 'bmp'){
$file = IMG_DIR.$imgarr['dirname'].'/'.$arr2[0].".".$ex;
}else{
$file = IMG_DIR.$imgarr['dirname'].'/'.$arr2[0].'_'.$arr2[1].".".$imgarr['extension'];
}
}
}
//获取缩略图文件名
//$imgpath = str_replace('.'.$imgarr['extension'],'.'.$arr[$arr_len-3],$imgpath);
$newfile = IMG_DIR.$imgpath;
if(imageResize($file,$newfile,$w,$h,$type)){
ob_clean();
<span style="color:#ffffff;"> </span><span style="background-color: rgb(255, 255, 153);">header("HTTP/1.1 200 OK");</span><span style="color:#ffffff;">
</span> header("Expires: ".gmdate("D, d M Y H:i:s", time()+315360000)." GMT");
header("Cache-Control: max-age=315360000");
header("Content-type: image/jpeg");
if($watermark==1){
imageWaterMark($newfile,9,'../images/watermark150.png');
}elseif($watermark==2){
imageWaterMark($newfile,9,'../images/watermark200.png');
}elseif($watermark==3){
imageWaterMark($newfile,9,'../images/watermark250.png');
}
echo file_get_contents($newfile);
}else{
ob_clean();
<span style="background-color: rgb(255, 255, 153);">header("HTTP/1.1 200 OK");</span>
header("Expires: ".gmdate("D, d M Y H:i:s", time()+315360000)." GMT");
header("Cache-Control: max-age=315360000");
header("Content-type: image/jpeg");
$newfile = IMG_DIR."images/default.png";
echo file_get_contents($newfile);
}
function realImage($url,$key = "imgkey"){
//本地要去除项目目录(autowebsite3g_svn)
$url = parse_url($url,PHP_URL_PATH);
if (substr($url, 0, 14) != "/_cache_images") {
return false;
} else {
$url = substr($url, 15);
}
$h = substr($url,0,2);
$url = substr($url,2);
$extend = pathinfo($url);
if($h=='H0'){
$real_url = Util::decrypt($extend['filename'],$key);
$real_url_info = pathinfo($real_url);
$file_info = explode('_', $real_url_info['filename']);
$widthandheight_info = explode('x', $file_info[count($file_info)-2]);
$new_widthandheight = strval(intval($widthandheight_info[0])/2)."x".strval(intval($widthandheight_info[1])/2);
return str_replace($file_info[count($file_info)-2],$new_widthandheight,$real_url);
}else{
return decrypt($extend['filename'],$key);
}
}
function decrypt2($txt, $key = 'anihc ctI') {
if($key == 'ewebsoft'){
return $txt;
}
$txt = str_replace("@","_",$txt);
$chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.";
$ikey = "-x6g6ZWm2G9g_vr0Bo.pOq3kRIxsZ6rm";
$knum = 0;
$i = 0;
$tlen = strlen($txt);
while (isset($key{$i}))
$knum +=ord($key{$i++});
$ch1 = $txt{$knum % $tlen};
$nh1 = strpos($chars, $ch1);
$txt = substr_replace($txt, '', $knum % $tlen--, 1);
if(strlen($txt)==0){
//TODO错误处理
echo '非法参数';exit;
}
$ch2 = $txt{$nh1 % $tlen};
$nh2 = strpos($chars, $ch2);
$txt = substr_replace($txt, '', $nh1 % $tlen--, 1);
if(strlen($txt)==0){
//TODO错误处理
echo '非法参数';exit;
}
$ch3 = $txt{$nh2 % $tlen};
$nh3 = strpos($chars, $ch3);
$txt = substr_replace($txt, '', $nh2 % $tlen--, 1);
$nhnum = $nh1 + $nh2 + $nh3;
$mdKey = substr(md5(md5(md5($key . $ch1) . $ch2 . $ikey) . $ch3), $nhnum % 8, $knum % 8 + 16);
$tmp = '';
$j = 0;
$k = 0;
$tlen = strlen($txt);
$klen = strlen($mdKey);
for ($i = 0; $i < $tlen; $i++) {
$k = $k == $klen ? 0 : $k;
$j = strpos($chars, $txt{$i}) - $nhnum - ord($mdKey{$k++});
while ($j < 0)
$j+=64;
$tmp .= $chars{$j};
}
$tmp = str_replace(array('-', '#', '.','@'),array('+', '/', '=','_'), $tmp);
return base64_decode($tmp);
}
/*
* 功能:PHP图片水印 (水印支持图片或文字)
* 参数:
*$groundImage 背景图片,即需要加水印的图片,暂只支持GIF,JPG,PNG格式;
*$waterPos水印位置,有10种状态,0为随机位置;
*1为顶端居左,2为顶端居中,3为顶端居右;
*4为中部居左,5为中部居中,6为中部居右;
*7为底端居左,8为底端居中,9为底端居右;
*$waterImage图片水印,即作为水印的图片,暂只支持GIF,JPG,PNG格式;
*$waterText文字水印,即把文字作为为水印,支持ASCII码,不支持中文;
*$textFont文字大小,值为1、2、3、4或5,默认为5;
*$textColor文字颜色,值为十六进制颜色值,默认为#FF0000(红色);
*
* 注意:Support GD 2.0,Support FreeType、GIF Read、GIF Create、JPG 、PNG
*$waterImage 和 $waterText 最好不要同时使用,选其中之一即可,优先使用 $waterImage。
*当$waterImage有效时,参数$waterString、$stringFont、$stringColor均不生效。
*加水印后的图片的文件名和 $groundImage 一样。
* 作者:longware @ 2004-11-3 14:15:13
*/
function imageWaterMark($groundImage,$waterPos=0,$waterImage="",$waterText="",$textFont=5,$textColor="#FF0000"){
$isWaterImage = FALSE;
$formatMsg = "暂不支持该文件格式,请用图片处理软件将图片转换为GIF、JPG、PNG格式。";
//读取水印文件
if(!empty($waterImage) && file_exists($waterImage)){
$isWaterImage = TRUE;
$water_info = getimagesize($waterImage);
$water_w = $water_info[0];//取得水印图片的宽
$water_h = $water_info[1];//取得水印图片的高
switch($water_info[2]){//取得水印图片的格式
case 1:$water_im = imagecreatefromgif($waterImage);break;
case 2:$water_im = imagecreatefromjpeg($waterImage);break;
case 3:$water_im = imagecreatefrompng($waterImage);break;
default:die($formatMsg);
}
}
//读取背景图片
if(!empty($groundImage) && file_exists($groundImage)){
$ground_info = getimagesize($groundImage);
$ground_w = $ground_info[0];//取得背景图片的宽
$ground_h = $ground_info[1];//取得背景图片的高
switch($ground_info[2])//取得背景图片的格式
{
case 1:$ground_im = imagecreatefromgif($groundImage);break;
case 2:$ground_im = imagecreatefromjpeg($groundImage);break;
case 3:$ground_im = imagecreatefrompng($groundImage);break;
default:die($formatMsg);
}
}else{
die("需要加水印的图片不存在!");
}
//水印位置
if($isWaterImage)//图片水印
{
$w = $water_w;
$h = $water_h;
$label = "图片的";
}else{//文字水印
$temp = imagettfbbox(ceil($textFont*5),0,"./cour.ttf",$waterText);//取得使用 TrueType 字体的文本的范围
$w = $temp[2] - $temp[6];
$h = $temp[3] - $temp[7];
unset($temp);
$label = "文字区域";
}
if( ($ground_w<$w) || ($ground_h<$h) ){
echo "需要加水印的图片的长度或宽度比水印".$label."还小,无法生成水印!";
return;
}
switch($waterPos){
case 0://随机
$posX = rand(0,($ground_w - $w));
$posY = rand(0,($ground_h - $h));
break;
case 1://1为顶端居左
$posX = 0;
$posY = 0;
break;
case 2://2为顶端居中
$posX = ($ground_w - $w) / 2;
$posY = 0;
break;
case 3://3为顶端居右
$posX = $ground_w - $w;
$posY = 0;
break;
case 4://4为中部居左
$posX = 0;
$posY = ($ground_h - $h) / 2;
break;
case 5://5为中部居中
$posX = ($ground_w - $w) / 2;
$posY = ($ground_h - $h) / 2;
break;
case 6://6为中部居右
$posX = $ground_w - $w;
$posY = ($ground_h - $h) / 2;
break;
case 7://7为底端居左
$posX = 0;
$posY = $ground_h - $h;
break;
case 8://8为底端居中
$posX = ($ground_w - $w) / 2;
$posY = $ground_h - $h;
break;
case 9://9为底端居右
$posX = $ground_w - $w - 10; // -10 是距离右侧10px 可以自己调节
$posY = $ground_h - $h - 0; // -10 是距离底部10px 可以自己调节
break;
default://随机
$posX = rand(0,($ground_w - $w));
$posY = rand(0,($ground_h - $h));
break;
}
//设定图像的混色模式
imagealphablending($ground_im, true);
if($isWaterImage){//图片水印
imagecopy($ground_im, $water_im, $posX, $posY, 0, 0, $water_w,$water_h);//拷贝水印到目标文件
}else{//文字水印
if( !emptyempty($textColor) && (strlen($textColor)==7) ){
$R = hexdec(substr($textColor,1,2));
$G = hexdec(substr($textColor,3,2));
$B = hexdec(substr($textColor,5));
}else{
die("水印文字颜色格式不正确!");
}
imagestring ( $ground_im, $textFont, $posX, $posY, $waterText, imagecolorallocate($ground_im, $R, $G, $B));
}
//生成水印后的图片
@unlink($groundImage);
switch($ground_info[2]){//取得背景图片的格式
case 1:imagegif($ground_im,$groundImage);break;
case 2:imagejpeg($ground_im,$groundImage);break;
case 3:imagepng($ground_im,$groundImage);break;
default:die($errorMsg);
}
//释放内存
if(isset($water_info)) unset($water_info);
if(isset($water_im)) imagedestroy($water_im);
unset($ground_info);
imagedestroy($ground_im);
}
注意上面高亮的部分,这是将返回状态从404改回到200。否则在第一次生成的时候,浏览器发现状态是404就不显示图片了。