首先,什麼話也不說,直接上效果圖。
檢視效果:
電影記憶》 外掛是在onethink 平臺上開發的一個用於記錄自己觀看電影、電視媒體的展示工具。
使用技術
-
bootstrap2.3.1 ui
-
onethink cmf框架
-
curl
-
QueryList php採集元件
-
又拍雲端儲存
功能介紹
-
搜尋電影媒體
-
看了電影
-
後臺列表(搜尋、刪除)
-
編輯觀看狀態、觀看時間
使用方法
-
後臺外掛列表中安裝、啟用外掛
-
外掛配置中設定又拍雲賬號
-
在要顯示的單個頁面 呼叫single鉤子
-
{:hook('single', array('name'=>'MovieLog'))}
4.0 後臺管理
4.1 後臺列表搜尋、刪除
4.2 後臺編輯單條記錄
4.3 後臺批量更新已看記錄的簡介
好了。下面開始從技術上講重點。
本外掛本人認為至少能夠起到的作用是:
-
作為一個onethink 最新帶後臺外掛的例子。
-
啟發upyun開發者如何對接介面。從採集圖片、上傳圖片、更新快取和批量進行資料管理更新。
-
啟發大家如何進行bootstrap的幾個外掛優雅的使用。
-
實時web進度條如何實現。
-
SAE 上對又拍雲的策略。
onethink 的介紹。大家看官網:http://www.onethink.cn/ 眾所周知,thinkphp是國內最流行的PHP框架。但是由於更新頻繁,使用者眾多。導致各種在此基礎上的產品太多,質量參差不齊。二次開發專案難度大、成本高。
因此,thinkphp官方決定出一個產品,制定一個標準,解決這個問題。雖然只要會php,任何問題都是能解決和實現的。那樣開發就像高考自由作文。文體不限。雖然自由,但是奇葩零分作文也多。
但是如果命個主題、限制個文體就容易多了。希望大家有空能研究下。
本人在官方onethink的基礎上移植了typecho的風格,製作了自己的部落格。也是開源的。作為程式設計師,技術部落格怎麼能用他人的呢?
接下來就一步步的講這次開發中的重點:
-
首先外掛的開發,大家可以參見onethink文件裡的外掛開發指南,和看本人錄製的外掛開發視訊。
又拍雲上傳介面的使用。
-
thinkphp3.2裡面的上傳類改進成驅動形式了。支援上傳時指定各種型別比如local、ftp、upyun、qiniu、sae、bcs之類的。
所以我就外掛裡面配置儲存了下驅動的必填項。然後用自己之前對接upyun的賬號裡又建了個空間bucket: movie-log。開始對接使用。
config.php
01return array(
02 'host'=>array(
03 'title'=>'又拍雲伺服器:',
04 'type'=>'text',
05 'value'=>'',
06 ),
07 'username'=>array(
08 'title'=>'又拍雲使用者:',
09 'type'=>'text',
10 'value'=>'',
11 ),
12 'password'=>array(
13 'title'=>'又拍雲密碼:',
14 'type'=>'password',
15 'value'=>'',
16 ),
17 'bucket'=>array(
18 'title'=>'又拍雲空間名稱:',
19 'type'=>'text',
20 'value'=>'MovieLog',
21 ),
22 'timeout'=>array(
23 'title'=>'又拍雲上傳超時時間:',
24 'type'=>'text',
25 'value'=>'90',
26 ),
27);
開始準備使用Upload驅動類的,後來由於上傳驅動裡有check檔案方法,裡面 is_uploaded_file 始終無法通過,畢竟不是傳統表單上傳的。為了不改核心程式碼,我把upyun的驅動放到外掛根目錄裡並對其進行修改。用裡面的save方法足以。後來加了個更新快取的方法。
/ThinkPHP/Library/Think/Upload.class.php
01 /**
02 * 檢查上傳的檔案
03 * @param array $file 檔案資訊
04 */
05 private function check($file) {
06 /* 檔案上傳失敗,捕獲錯誤程式碼 */
07 if ($file['error']) {
08 $this->error($file['error']);
09 return false;
10 }
11
12 /* 無效上傳 */
13 if (empty($file['name'])){
14 $this->error = '未知上傳錯誤!';
15 }
16
17 /* 檢查是否合法上傳 */
18 if (!is_uploaded_file($file['tmp_name'])) {
19 $this->error = '非法上傳檔案!';
20 return false;
21 }
在我的外掛MovieLogModel裡寫了個方法,進行圖片的上傳。
01 include_once ONETHINK_ADDON_PATH.'MovieLog/function.php';
02 $dir = ONETHINK_ADDON_PATH.'MovieLog/Upload/';
03 if(APP_MODE == 'sae')
04 $dir = SAE_TMP_PATH;
05 $UpyunConfig = get_addon_config('MovieLog');
06 $uploader = new Upyun('/', $UpyunConfig);
07 $name = strstr($this->data['alt'],'subject/');
08 $name = ltrim($name,'subject/');
09 $name = trim($name,'/');
10 if(!is_dir($dir))
11 @mkdir($dir);
12 $images = array(
13 's'=>$this->curl_download($images['small'],"{$dir}s_{$name}.jpg"),
14 'm'=>$this->curl_download($images['medium'],"{$dir}m_{$name}.jpg"),
15 'l'=>$this->curl_download($images['large'],"{$dir}l_{$name}.jpg")
16 );
17 foreach ($images as $key => $value) {
18 $file = array(
19 'name' =>"{$key}_{$name}.jpg",
20 'savepath'=>'',
21 'savename'=>"{$key}_{$name}.jpg",
22 'type' =>"image/jpeg",
23 'size' =>filesize($value),
24 'tmp_name' =>$value,
25 'md5'=>md5_file($value),
26 'error' =>0,
27 );
28 $info = $uploader->save($file);
29 if($info)
30 @unlink($value);
31 }
32 $refresh = array();
33 $return = array('small'=>"s_{$name}.jpg",'medium'=>"m_{$name}.jpg",'large'=>"l_{$name}.jpg");
34 foreach($return as $r){
35 $refresh[] = movie_cover($r);
36 }
37
38 $uploader->refreshCache(join('\n', $refresh));
39 return serialize($return);
40 }
所以外掛圖片實際上是通過儲存遠端圖片到本地,然後用驅動上傳到upyun空間裡,然後刪除的。大家都知道海量圖片佔用本地磁碟大,且訪問慢。這時候又拍雲就起到作用了。
我首頁200多部電影的封面,依然開啟嗖嗖的。
這裡有兩點要注意。一個是圖片重複上傳的問題,由於我的圖片採集下來規則是定的一個圖片不會重複,有個對應的電影id。所以無需判斷。正常為了節省流量和時間。先判斷空間上是否有同樣的圖片,有就返回圖片地址資訊,沒有就才上傳。判斷唯一通過md5_file和sha1_file函式判斷就可以了。
curl 採集用的QueryList。注意選擇器正確,無法找到內容,去掉rang引數
再一個就是圖片快取的問題。圖片上傳後,通過http://空間.b0.upaiyun.com/檔名 訪問,不一定立馬能訪問到。這個需要手動去重新整理它。雖然又拍雲管理空間裡有傳多個連結(換行)然後更新快取的工具。從人性化的角度來說,這應該是自動的。或者訪問時後觸發的。因此我擴充套件裡onethink的Upyun驅動類加上了重新整理的方法。然後開始是在外掛訪問的前臺首頁,繫結圖片error事件,去ajax重新整理的。
01 //重新整理upaiyun圖片快取
02 $('img[src*="http://{$config.bucket}"]').error(function(e){
03 setTimeout(function () {
04 var url = "{:addons_url('MovieLog://MovieLog/refreshImg')}";
05 var _this = this;
06 this.src = '';
07 $(this).parents('.post-li').addClass('mega-loading');
08 $.ajax({
09 url: url,
10 data: {file : this.src},
11 success:function(data){
12 if(data.status)
13 _this.src = _this.src;
14 $(this).parents('.post-li').removeClass('mega-loading');
15 }
16 });
17 }, 1000);
18
19 });
但是由於圖太多了,不斷ajax 有可能導致瀏覽器卡死。後來在除錯sae上上傳的時候傳完手動重新整理了一次。上面程式碼中
1$uploader->refreshCache(join('\n', $refresh));
就是呼叫介面重新整理。
批量進行電影簡介更新。首先批量只是小批量,只選取了觀看後的記錄,並且要更新的summary欄位is null or = ''的記錄。因為本外掛是在搜尋時候採集資料,更新觀看狀態欄位時候採集圖片和寫如簡介的。為了效果,手動加入記錄後。已經有3000條了。全部更新的話太慢。想想一個記錄查curl都要2s左右。
然後就是通過一個按鈕彈出更新的視窗。進行第一次查詢總更新記錄集合,快取主,在返回的第一次查詢裡返回更新的table裡的tr 的 process js函式。用eval 執行一次。然後執行的js函式裡再呼叫ajax重新請求下一個和改進度條寬度。最後一次返回判斷是否結束,將進度條寬度直接設滿。
js端:
01('#batch_update_btn').click(function(){
02 var url = $(this).attr('url');
03 $('#myModal').html($('#loading_tpl').html());
04 var $btn = $(this);
05 $btn.button('loading');
06 $('#myModal').modal('show');
07 $.ajax({
08 url: url,
09 success:function(data){
10 eval(data);
11 },
12 error:function(XMLHttpRequest, textStatus, errorThrown){
13 $('#myModal').html('網路出錯了');
14 }
15 });
16 $('#myModal').modal('show');
17 return false;
18 });
01function process(id, msg){
02 var $btn = $('#batch_update_btn');
03 if(id != 0){
04 var url = $btn.attr('url');
05 $('#myModal .progress .bar').width(function(n,c){
06 return c+5;
07 });
08 $('#file_list tbody').prepend(msg);
09 $.ajax({
10 url: url,
11 data: {id: id},
12 success:function(data){
13 eval(data);
14 },
15 error:function(XMLHttpRequest, textStatus, errorThrown){
16 $('#myModal').html('網路出錯了');
17 }
18 });
19 }else{
20 $('#myModal .progress .bar').width('670');
21 $('#file_list').before('<p>全部更新完畢</p>');
22 $btn.button('reset');
23 }
24 }
php端方法
01 //批量更新簡介
02 public function update_batch(){
03 $id = I('id', 0);
04 include_once ONETHINK_ADDON_PATH.'MovieLog/DoubanMovie.class.php';
05 $obj = new \DoubanMovie();
06 if($id == 0){
07 $movie = D('Addons://MovieLog/Movie');
08 $list = $movie->where("(summary is null OR summary = '') AND is_published = 1")->getField('id,id,title');
09 $record = array_pop($list);
10 $id = $record['id'];
11 }else{
12 $list = session('batch_list');
13 $record = $list[$id];
14 unset($list[$id]);
15 }
16 $msg = "<tr><td>{$record['id']}</td><td>{$record['title']}</td><td class='success'> √</td></tr>";
17
18 $detail = $obj->subject($id);
19 $movie = D('Addons://MovieLog/Movie');
20 if($detail['summary'] && $movie->save(array('id'=>$id, 'summary'=>$detail['summary'])) !== false)
21 $result = "<td class='success'> √</td>";
22 else
23 $result = "<td class='error'> X</td>";
24 $msg = "<tr><td>{$record['id']}</td><td>{$record['title']}</td>{$result}</tr>";
25 if(count($list)){
26 $new = array_slice($list, 0 ,1);
27 echo "process({$new[0]['id']}, \"{$msg}\");";
28 session('batch_list', $list);
29 }else{
30 session('batch_list', null);
31 exit("process(0,'');");
32 }
33 }
c bootstrap外掛使用:
首先用了ScrollSpy 這個js元件。用於首頁月份隨內容高度變化而高亮的。這個效果是參考 點點網的 archive歸檔效果。這個外掛注意點是他高亮的元素必須是.nav 的li 並且加的是active類名。在監聽容器上可以指定偏移高度。
然後是popover元件。預設的在移動到浮動窗上的容易消失。找了段別人寫好的manual 事件的
01//下方圖片牆提示
02 $('#mega-content .post-li').popover({
03 trigger:'manual',
04 delay:{show:1000, hide: 1000},
05 content:function(e){
06 var src = $(this).attr('src');
07 var summary = $(this).attr('summary');
08 return "<div class='cover_popover'><img src='"+src+"'><p>"+summary+"</p></div>";
09 },
10 html : true,
11 title: this.title
12 }).on("mouseover", function () {
13 var _this = this;
14 $(this).popover("show");
15 $(this).siblings(".popover").on("mouseleave", function () {
16 $(_this).popover('hide');
17 });
18 }).on("mouseleave", function () {
19 var _this = this;
20 setTimeout(function () {
21 if (!$(".popover:hover").length) {
22 $(_this).popover("hide")
23 }
24 }, 100);
25 });
最後是彈窗。bootstrap的彈窗美觀,但是提供的介面少。就那麼幾個方法。不過在我的努力下實現了動態載入內容。
一種是指定remote屬性。但是這種不能動態傳引數。
第二種就是我用的,先頁面上放容器,然後彈出時候ajax返回頁面內容。
注意:1.彈窗樣式 頁面上控制。2.預設彈窗會快取住,繫結hide事件,將載入的頁面刪除掉。
1$('#myModal').on('shown.bs.modal', function (e) {
2
3}).on('hidden.bs.modal',function(e){
4 $(this).removeData('bs.modal');
5});
d 實時進度條程式碼上面有了,就是輪詢ajax 設定進度條寬度。
e sae 上開始參考官方手冊說有wrapper saekv://、 saemc 都試了沒用。最後發現有臨時目錄常量 判斷環境sae時改下臨時儲存目錄就行了。本來還準備曲線救國用ftp介面呢!話說sae第三方服務有又拍雲,結果不能開通。怒了。
1 if(APP_MODE == 'sae')
2 $dir = SAE_TMP_PATH;
之前自己寫測試寫入可以 ajax訪問就不行了。最後發現是快取的問題。最後上傳完就重新整理快取,什麼疑難雜症都沒了。O(∩_∩)O哈哈~
好了,總結就這麼多,希望大家多給我投票 http://upyun.gitcafe.com/projects?category=top50 onethink-MovieLog-for-upyun 那個。“深度雲服務,編譯新未來”,歡迎大家關注比賽。