天天看點

ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)

版權屬于: Postbird - There I am , in the world more exciting!

原文位址: http://www.ptbird.cn/thinkphp-image-water-sql/

轉載時必須以連結形式注明原始出處及本聲明。

TPImageWater 是ThinkPHP擴充的用來自動化為已經上傳部分圖檔加水印的應用

這是第二部分,也就是從資料庫中讀取圖檔存儲位址的字段内容,來實作圖檔添加水印。

如果想看之前的部分請參照:

在1的基礎上,根據客戶的需求,進行一個完善,具體的使用方法和原理介紹主要在第二篇文章以及代碼中的注釋(這估計是我寫的最多的注釋了)

1、ThinkPHP為已經上傳的圖檔添加水印的插件【原始版本】

2、ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(檔案周遊部分)

應用思想來源:

本來之前寫了周遊檔案夾方法來為所有的圖檔添加【響應式水印】,所謂響應式就是根據标準圖檔的大小,标準水印的大小和位置,自動化處理檔案夾内的所有圖檔,以達到水印位置和大小符合實際不同圖檔大小的要求。

基于周遊檔案夾的方法存在的問題,也就是客戶的最新要求:隻處理上傳的産片圖檔,一些文章中的雜七雜八的圖檔不需要添加水印。

因為系統設計過程中,圖檔上傳基本是以時間點為檔案夾,并不區分圖檔的分類。

是以從資料庫中讀取字段值是一個很不錯的方法。

關鍵解決:

1、需要處理的圖檔不一定隻在一張表中的一個字段,是以需要準備多個字段的處理。

2、檔案周遊方法中,我們需要周遊檔案夾的所有内容,并進行相關的圖檔檔案的篩選,但是從資料庫讀取字段,主要面臨的問題是資料庫字段中獨處的資料能否直接用于圖檔位址的讀取,而且可能存在空值

例如:我在應用的過程中,資料庫中存的形式如下【/Uploads/image/20150911/55f2424d5a54d.jpg】

ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)
是以需要對字段進行處理,而在代碼中,循環的時候預留了處理的代碼空間,可以根據實際上自己的需求進行處理。
ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)

關鍵算法:

1、為了實作多個資料庫字段内容的讀取,我設計的第一個參數是數組,主要的實作也在代碼的注釋中(代碼注釋是一切)。
ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)

2、(重複一下之前文章提到的水印算法)

因為需要根據标準圖檔的大小和水印圖檔的大小以及位置計算出實際上水印圖檔的大小和位置,是以需要每次計算和生成新的水印并添加到圖檔中。

由于thinkphp本身的性質,我們每次對水印圖檔也就是【water.jpg】進行處理生成新的【tmpWater.jpg】,每次都采用覆寫形式的處理,是以到最後處理完成結束了,隻會多一張【tmpwater.jpg】,而沒有其他的變化。

ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)

提提之前文章的内容:

之前文章也就是【檔案周遊部分】主要介紹了實作響應式水印的關鍵算法。

其中也提到了,ThinkPHP3.2實際上已經支援locate(x,y)的參數,也就是根據橫縱坐标實作水印圖檔的位置,不是像官方手冊介紹的隻有那幾種方法。我也不知道為什麼手冊不更新。

TPImageWater實作的效果:

ThinkPHP自動化為已經上傳的圖檔添加「 響應式」水印(資料庫字段部分)

代碼檢視(檔案周遊和sql資料庫字段整合在一起):

下面代碼可能輸出過程中html标簽替代問題,建議直接下載下傳gitosc或者是github的源碼檢視

碼雲:https://git.oschina.net/postbird/TPImageWater

github:https://github.com/postbird/TPImageWater.git

/*
*
*	使用thinkphp 循環為已經上傳的圖檔添加水印
*
*   從資料庫讀出部分圖檔的位址并轉換成本地位址進行操作
*   從資料庫中讀圖檔,省去了周遊和篩選過濾的環節
*   相比周遊出來在進行比對,相對快速和友善
*   最終的函數沒有本質的差別,隻有參數變化了。
*   詳細請看參數注釋
*
*	by postbird
*
*   http://www.ptbird.cn
*
*	2016-10-21
*
*	license MIT
*
*/
namespace Home\Controller;
use Think\Controller;

//處理速度讓人崩潰200張要30s
//是以需要設定最大執行時間為無限
ini_set('max_execution_time','0');

class SqlImageWaterController extends Controller {
    //禁止随便通路
    //通路的時候後面必須加上該字段的參數值
    public function check($code){
        $key="postbird";
        if(md5($key)!=md5($code)){
            return false;
        }else{
            return true;
        }
    }
    public function index(){
        $code=$_GET['code'];
        if(!$this->check($code)){
            echo "no such file";
            return;
            exit();
            die;
        }
       
        //兩個參數 第一個是要處理的檔案位址的合輯
        //從資料庫中讀取檔案位址,可能不是操作一個表,是以需要将表和要讀取的字段名稱作為數組參數傳遞
        //第二個是水印圖檔的本地位址
        //我要處理三張表 每張表取出圖檔字段
        //資料庫已經配置了表字首
        //預設的存儲形式是  /Uploads/image/20150920/55fe9a083ca65.jpg 
            //因為我前面沒有 . ,是以在後面進行處理的時候每張圖檔的前面加了.
            // 需要根據資料庫字段進行設定 參照113-116行代碼,在這裡面設定就可以
        $imgSqlArr=array();
        $imgSqlArr[0]['table']='article_page';
        $imgSqlArr[0]['field']='cover';
        $imgSqlArr[1]['table']='item';
        $imgSqlArr[1]['field']='img';
        $imgSqlArr[2]['table']='item_img';
        $imgSqlArr[2]['field']='img';
    	$this->imageWater($imgSqlArr,'./Uploads/water.jpg');
        $this->display();
    }
    //替換得到所有圖檔
    //傳回數組,需要對數組進行組合    
    public function getImgBySql($imgSqlArr){
        //因為不确定處理幾張表,是以每次都組合一次數組
        //要傳回的結果
            $res=array();
            for($i=0;$i<count($imgSqlArr);$i++){ $tmpRes[$i]=array(); //傳回需要的列的資料 $tmpRes[$i]=M($imgSqlArr[$i]['table'])->getField($imgSqlArr[$i]['field'],true);
                $res=array_merge($res,$tmpRes[$i]);
            }
            //傳回資料 但是裡面資料可能存在空值 空值需要跳過
            // p($res);exit();
            return $res;
    }
    //進行水印處理
    // 參數是  檔案sql數組-imgSqlArr  water
    // water是水印的位置
    public function imageWater($imgSqlArr,$water){
         if(!is_array($imgSqlArr)){
            echo"參數隻能為數組";
            die;
        }
    	//得到所有需要處理的圖檔
    	$images=$this->getImgBySql($imgSqlArr);
        p($images);
    	//使用Image類
		$Image = new \Think\Image(); 
        //水印位置是存放在根目錄下 water.png 可以根據自己需要改
        //設定臨時的水印檔案與water.png在同一目錄,為了圖省事可以放在根目錄
        //下面使用分割拼接主要是為了通用性,可以根據實際的檔案夾要求直接寫死
        //現在的做法是将water.png分出來,再把water截取後面water.png的長度,就是目錄
        //截取的前幾位字元串接上tmpWater.png就是字元串
        $tmpWaterArr=explode("/",$water);
        $tmpWater="tmpWater.jpg";
        $tmpWater=substr($water, 0,strlen($tmpWaterArr[count($tmpWaterArr)-1])+1).$tmpWater;
        // 上面表達式的應用從  "./Uploads/water.png" 又生成 "./Uploads/tmpWater.png"
        // 進而實作了響應式的臨時水印的位置,當然如果嫌麻煩直接根目錄也沒啥
        //因為是預設覆寫源檔案的,是以即使每次生成tmpwater.png,最後也隻會多處一張圖檔

        //循環周遊圖檔,并進行操作
		for($i=0;$i<count($images);$i++){ //判斷資料庫的空值進行跳過 if(strlen($images[$i])==0){ continue; }else{ //有了就不需要了 //我的資料庫裡面字段存的時候沒有. //是以需要在這裡操作一下 $images[$i]=".".$images[$i]; } //同樣儲存到檔案中的過程中需要進行 轉碼成GB2312 $images[$i] = iconv('UTF-8','GB2312',$images[$i]); //打開原圖檔預設添加水印到左上角 并儲存為原來位置和名稱 自動覆寫 //因為每個圖檔的大小不确定,是以需要根據圖檔大小進行位置的确定 //關于水印位置和選項可以參照 thinkphp3.2 手冊 // http://document.thinkphp.cn/manual_3_2.html#image $imageInfo=$Image->open($images[$i]);

            //标準圖檔寬度和高度
            $bw=430;
            $bh=430;
            //标準的水印大小
            $bww=130;
            $bwh=50;
            //标準的水印距離标準的圖檔的右方和下方間距
            //(總是以水印左上角為起點)
            $bsubw=30;
            $bsubh=80;
            //是以可以得出标準的水印的位置應該是
            $bpx=$bw-$bww-$bsubw;//270
            $bpy=$bh-$bwh-$bsubh;//300
            //擷取實際圖檔(打開的圖檔)的大小
            $imgw=$imageInfo->width();
            $imgh=$imageInfo->height();
            //實際水印的大小根據距離的位置($bsub*)與圖檔($b*)的比例計算出來
            $ww=round($bww*($imgw/$bw));
            $wh=round($bwh*($imgh/$bh));
            //先前沒有考慮實際上距離的位置也應随着圖檔大小的變化而變化
            $subw=round($bsubw*($imgw/$bw));
            $subh=round($bsubh*($imgh/$bh));
            //【正确】實際水印的位置按照之前的預算,距離底部和右邊的距離理論上是不變的,
            //  這樣子才能維持原來水印的位置,隻不過現在水印的大小變了
            $px=$imgw-$ww-$subw;
            $py=$imgh-$wh-$subh;
            //水印location的位置
            $location=array($px,$py);
            //計算水印的大小  生成縮略圖
            //大小是根據上面的比例算出來的
            //這裡要用到我們上面截取生成的臨時水印的檔案
            //這個處理是為了解決thinkphp再上傳儲存後$tmpWater就不是string類型
            //是以找了一個臨時變量來處理
            $tmpStr=$tmpWater;
            $Image->open($water)->thumb($ww, $wh,\Think\Image::IMAGE_THUMB_FILLED)->save($tmpStr);
            //這裡本來寫了再次打開tmpWater
            //thinkphp的機理就是save函數調用後,tmpWater自動成為Iamge的對象
            //是以不需要打開
            
            //測試最後的各個圖檔的大小和位置
            // echo("标準    圖檔 ".$bw." ".$bh." "." 水印 ".$bww." ".$bwh." 位置 ".$bpx." ".$bpy."
");
            // echo("現在    圖檔 ".$imgw." ".$imgh." "." 水印 ".$ww." ".$wh." 位置 ".$px." ".$py."
");
            // exit();

			//目前使用的水印就是上面生成的比例大小的水印檔案
            //water的第一個參數是字元串類型的
            //這裡我本來使用了imageInfo,因為之前已經打開過一張了
            //但是發現每次儲存都儲存成為現在的水印圖檔
            //不知道什麼原因,無奈隻能重新打開和儲存
			if($Image->open($images[$i])->water($tmpWater,$location,80)->save($images[$i])){
              echo "< p >成功處理第".$i."張圖檔...";
            }
        }
        echo "< hr >< h1 >圖檔處理完成....< h1 >";
        echo "powered by  < a href='http://www.ptbird.cn' >postbird ";
    }
}