《电影记忆》-又拍云比赛作品总结

首先,什么话也不说,直接上效果图。

查看效果: 首页

搜索关键字

选择观看电影,日期


电影记忆》 插件是在onethink 平台上开发的一个用于记录自己观看电影、电视媒体的展示工具。

使用技术

  • bootstrap2.3.1 ui

  • onethink cmf框架

  • curl

  • QueryList php采集组件

  • 又拍云存储

功能介绍

  1. 搜索电影媒体

  2. 看了电影

  3. 后台列表(搜索、删除)

  4. 编辑观看状态、观看时间

使用方法

  1. 后台插件列表中安装、启用插件 插件列表

  2. 插件配置中设置又拍云账号 配置插件

  3. 在要显示的单个页面 调用single钩子


    1. {:hook('single', array('name'=>'MovieLog'))}

4.0 后台管理

4.1 后台列表搜索、删除 03120053_Ekve.jpg

4.2 后台编辑单条记录 编辑记录

4.3 后台批量更新已看记录的简介03120053_TXEp.jpg

好了。下面开始从技术上讲重点。

本插件本人认为至少能够起到的作用是:

  1. 作为一个onethink 最新带后台插件的例子。

  2. 启发upyun开发者如何对接接口。从采集图片、上传图片、更新缓存和批量进行数据管理更新。

  3. 启发大家如何进行bootstrap的几个插件优雅的使用。

  4. 实时web进度条如何实现。

  5. SAE 上对又拍云的策略。

onethink 的介绍。大家看官网:http://www.onethink.cn/ 众所周知,thinkphp是国内最流行的PHP框架。但是由于更新频繁,使用者众多。导致各种在此基础上的产品太多,质量参差不齐。二次开发项目难度大、成本高。

因此,thinkphp官方决定出一个产品,制定一个标准,解决这个问题。虽然只要会php,任何问题都是能解决和实现的。那样开发就像高考自由作文。文体不限。虽然自由,但是奇葩零分作文也多。

但是如果命个主题、限制个文体就容易多了。希望大家有空能研究下。

本人在官方onethink的基础上移植了typecho的风格,制作了自己的博客。也是开源的。作为程序员,技术博客怎么能用他人的呢?

接下来就一步步的讲这次开发中的重点:

  1. 首先插件的开发,大家可以参见onethink文档里的插件开发指南,和看本人录制的插件开发视频

又拍云上传接口的使用。

  thinkphp3.2里面的上传类改进成驱动形式了。支持上传时指定各种类型比如local、ftp、upyun、qiniu、sae、bcs之类的。

所以我就插件里面配置存储了下驱动的必填项。然后用自己之前对接upyun的账号里又建了个空间bucket: movie-log。开始对接使用。

config.php

return array(
	'host'=>array(
		'title'=>'又拍云服务器:',
		'type'=>'text',
		'value'=>'',
	),
	'username'=>array(
		'title'=>'又拍云用户:',
		'type'=>'text',
		'value'=>'',
	),
	'password'=>array(
		'title'=>'又拍云密码:',
		'type'=>'password',
		'value'=>'',
	),
	'bucket'=>array(
		'title'=>'又拍云空间名称:',
		'type'=>'text',
		'value'=>'MovieLog',
	),
	'timeout'=>array(
		'title'=>'又拍云上传超时时间:',
		'type'=>'text',
		'value'=>'90',
	),
);

     开始准备使用Upload驱动类的,后来由于上传驱动里有check文件方法,里面 is_uploaded_file 始终无法通过,毕竟不是传统表单上传的。为了不改核心代码,我把upyun的驱动放到插件根目录里并对其进行修改。用里面的save方法足以。后来加了个更新缓存的方法。

    /ThinkPHP/Library/Think/Upload.class.php 

    /**
     * 检查上传的文件
     * @param array $file 文件信息
     */
    private function check($file) {
        /* 文件上传失败,捕获错误代码 */
        if ($file['error']) {
            $this->error($file['error']);
            return false;
        }

        /* 无效上传 */
        if (empty($file['name'])){
            $this->error = '未知上传错误!';
        }

        /* 检查是否合法上传 */
        if (!is_uploaded_file($file['tmp_name'])) {
            $this->error = '非法上传文件!';
            return false;
        }

在我的插件MovieLogModel里写了个方法,进行图片的上传。

        include_once ONETHINK_ADDON_PATH.'MovieLog/function.php';
        $dir = ONETHINK_ADDON_PATH.'MovieLog/Upload/';
        if(APP_MODE == 'sae')
            $dir = SAE_TMP_PATH;
        $UpyunConfig = get_addon_config('MovieLog');
        $uploader = new Upyun('/', $UpyunConfig);
        $name = strstr($this->data['alt'],'subject/');
        $name = ltrim($name,'subject/');
        $name = trim($name,'/');
        if(!is_dir($dir))
            @mkdir($dir);
        $images = array(
            's'=>$this->curl_download($images['small'],"{$dir}s_{$name}.jpg"),
            'm'=>$this->curl_download($images['medium'],"{$dir}m_{$name}.jpg"),
            'l'=>$this->curl_download($images['large'],"{$dir}l_{$name}.jpg")
        );
        foreach ($images as $key => $value) {
            $file = array(
                'name'   =>"{$key}_{$name}.jpg",
                'savepath'=>'',
                'savename'=>"{$key}_{$name}.jpg",
                'type'   =>"image/jpeg",
                'size'   =>filesize($value),
                'tmp_name' =>$value,
                'md5'=>md5_file($value),
                'error' =>0,
            );
            $info = $uploader->save($file);
            if($info)
                @unlink($value);
        }
        $refresh = array();
        $return = array('small'=>"s_{$name}.jpg",'medium'=>"m_{$name}.jpg",'large'=>"l_{$name}.jpg");
        foreach($return as $r){
        	$refresh[] = movie_cover($r);
        }
        
        $uploader->refreshCache(join('\n', $refresh));
        return serialize($return);
    }

所以插件图片实际上是通过保存远程图片到本地,然后用驱动上传到upyun空间里,然后删除的。大家都知道海量图片占用本地磁盘大,且访问慢。这时候又拍云就起到作用了。

我首页200多部电影的封面,依然打开嗖嗖的。

这里有两点要注意。一个是图片重复上传的问题,由于我的图片采集下来规则是定的一个图片不会重复,有个对应的电影id。所以无需判断。正常为了节省流量和时间。先判断空间上是否有同样的图片,有就返回图片地址信息,没有就才上传。判断唯一通过md5_file和sha1_file函数判断就可以了。

curl 采集用的QueryList。注意选择器正确,无法找到内容,去掉rang参数

再一个就是图片缓存的问题。图片上传后,通过http://空间.b0.upaiyun.com/文件名 访问,不一定立马能访问到。这个需要手动去刷新它。虽然又拍云管理空间里有传多个链接(换行)然后更新缓存的工具。从人性化的角度来说,这应该是自动的。或者访问时后触发的。因此我扩展里onethink的Upyun驱动类加上了刷新的方法。然后开始是在插件访问的前台首页,绑定图片error事件,去ajax刷新的。

                //刷新upaiyun图片缓存
                $('img[src*="http://{$config.bucket}"]').error(function(e){
                    setTimeout(function () {
                        var url = "{:addons_url('MovieLog://MovieLog/refreshImg')}";
                        var _this = this;
                        this.src = '';
                        $(this).parents('.post-li').addClass('mega-loading');
                        $.ajax({
                            url: url,
                            data: {file : this.src},
                            success:function(data){
                                if(data.status)
                                    _this.src = _this.src;
                                $(this).parents('.post-li').removeClass('mega-loading');
                            }
                        });
                    }, 1000);

                });

但是由于图太多了,不断ajax 有可能导致浏览器卡死。后来在调试sae上上传的时候传完手动刷新了一次。上面代码中

$uploader->refreshCache(join('\n', $refresh));

就是调用接口刷新。

批量进行电影简介更新。首先批量只是小批量,只选取了观看后的记录,并且要更新的summary字段is null or = ''的记录。因为本插件是在搜索时候采集数据,更新观看状态字段时候采集图片和写如简介的。为了效果,手动加入记录后。已经有3000条了。全部更新的话太慢。想想一个记录查curl都要2s左右。

然后就是通过一个按钮弹出更新的窗口。进行第一次查询总更新记录集合,缓存主,在返回的第一次查询里返回更新的table里的tr 的 process js函数。用eval 执行一次。然后执行的js函数里再调用ajax重新请求下一个和改进度条宽度。最后一次返回判断是否结束,将进度条宽度直接设满。

js端:

('#batch_update_btn').click(function(){
            var url = $(this).attr('url');
            $('#myModal').html($('#loading_tpl').html());
            var $btn = $(this);
            $btn.button('loading');
            $('#myModal').modal('show');
            $.ajax({
                url: url,
                success:function(data){
					eval(data);
                },
                error:function(XMLHttpRequest, textStatus, errorThrown){
                    $('#myModal').html('网络出错了');
                }
            });
            $('#myModal').modal('show');
            return false;
        });

        

function process(id, msg){
        var $btn = $('#batch_update_btn');
        if(id != 0){
            var url = $btn.attr('url');
            $('#myModal .progress .bar').width(function(n,c){
                return c+5;
            });
            $('#file_list tbody').prepend(msg);
            $.ajax({
                url: url,
                data: {id: id},
                success:function(data){
                    eval(data);
                },
                error:function(XMLHttpRequest, textStatus, errorThrown){
                    $('#myModal').html('网络出错了');
                }
            });
        }else{
            $('#myModal .progress .bar').width('670');
            $('#file_list').before('<p>全部更新完毕</p>');
            $btn.button('reset');
        }
    }

php端方法

    //批量更新简介
    public function update_batch(){
		$id = I('id', 0);
        include_once ONETHINK_ADDON_PATH.'MovieLog/DoubanMovie.class.php';
        $obj = new \DoubanMovie();
        if($id == 0){
            $movie = D('Addons://MovieLog/Movie');
            $list = $movie->where("(summary is null OR summary = '') AND is_published = 1")->getField('id,id,title');
            $record = array_pop($list);
            $id = $record['id'];
        }else{
        	$list = session('batch_list');
            $record = $list[$id];
            unset($list[$id]);
        }
        $msg = "<tr><td>{$record['id']}</td><td>{$record['title']}</td><td class='success'> √</td></tr>";

  		$detail = $obj->subject($id);
        $movie = D('Addons://MovieLog/Movie');
        if($detail['summary'] && $movie->save(array('id'=>$id, 'summary'=>$detail['summary'])) !== false)
            $result = "<td class='success'> √</td>";
        else
            $result = "<td class='error'> X</td>";
        $msg = "<tr><td>{$record['id']}</td><td>{$record['title']}</td>{$result}</tr>";
        if(count($list)){
            $new = array_slice($list, 0 ,1);
            echo "process({$new[0]['id']}, \"{$msg}\");";
            session('batch_list', $list);
        }else{
            session('batch_list', null);
            exit("process(0,'');");
        }
    }

c bootstrap插件使用:

  首先用了ScrollSpy 这个js组件。用于首页月份随内容高度变化而高亮的。这个效果是参考 点点网的 archive归档效果。这个插件注意点是他高亮的元素必须是.nav 的li 并且加的是active类名。在监听容器上可以指定偏移高度。

  然后是popover组件。默认的在移动到浮动窗上的容易消失。找了段别人写好的manual 事件的

  

//下方图片墙提示
                $('#mega-content .post-li').popover({
                    trigger:'manual',
                    delay:{show:1000, hide: 1000},
                    content:function(e){
                        var src = $(this).attr('src');
                        var summary = $(this).attr('summary');
                        return "<div class='cover_popover'><img src='"+src+"'><p>"+summary+"</p></div>";
                    },
                    html : true,
                    title: this.title
                }).on("mouseover", function () {
                    var _this = this;
                    $(this).popover("show");
                    $(this).siblings(".popover").on("mouseleave", function () {
                        $(_this).popover('hide');
                    });
                }).on("mouseleave", function () {
                    var _this = this;
                    setTimeout(function () {
                        if (!$(".popover:hover").length) {
                            $(_this).popover("hide")
                        }
                    }, 100);
                });

  最后是弹窗。bootstrap的弹窗美观,但是提供的接口少。就那么几个方法。不过在我的努力下实现了动态加载内容。

  一种是指定remote属性。但是这种不能动态传参数。

 第二种就是我用的,先页面上放容器,然后弹出时候ajax返回页面内容。

  注意:1.弹窗样式 页面上控制。2.默认弹窗会缓存住,绑定hide事件,将加载的页面删除掉。

$('#myModal').on('shown.bs.modal', function (e) {

}).on('hidden.bs.modal',function(e){
     $(this).removeData('bs.modal');
});

d 实时进度条代码上面有了,就是轮询ajax 设置进度条宽度。

e sae 上开始参考官方手册说有wrapper saekv://、 saemc 都试了没用。最后发现有临时目录常量 判断环境sae时改下临时存储目录就行了。本来还准备曲线救国用ftp接口呢!话说sae第三方服务有又拍云,结果不能开通。怒了。

        if(APP_MODE == 'sae')
            $dir = SAE_TMP_PATH;

之前自己写测试写入可以 ajax访问就不行了。最后发现是缓存的问题。最后上传完就刷新缓存,什么疑难杂症都没了。O(∩_∩)O哈哈~ 


好了,总结就这么多,希望大家多给我投票 http://upyun.gitcafe.com/projects?category=top50  onethink-MovieLog-for-upyun 那个。“深度云服务,编译新未来”,欢迎大家关注比赛。



转载于:https://my.oschina.net/u/136583/blog/232711

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值