2016年10月

git本地拉取远程分支

首先git clone一个git库,
其次在本地新建一个分支

git checkout -b devlocal

然后设置本地git文件,将本地分支和远程分支关联起来 (我有个远程分支叫做dev)

git branch --set-upstream-to=origin/dev devlocal

再git pull 就能获取到远程git dev分支上的东西到本地的devlocal这个分支上了

还有一个是将本地分支推送到远程分支前要做的处理(远程不存在该分支)

git push --set-upstream origin new

yii2路由重写方案

apache

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

nginx

location / {  
    if (!-e $request_filename){  
        rewrite ^/(.*) /index.php last;  
    }  
} 

conf/web.php 中的 ‘components’数组是这样的

'urlManager' => [
            'enablePrettyUrl' => true,
            'showScriptName' => false,
            'rules' => [
            ],
        ],

小程序初次接触(1)

刚开始接触vue.js没多久就收到小程序内侧的信息,看了看文档两者关联性很强,于是就两者一块学了

首先安装小程序的开发工具,虽然我们没有邀请id但是我们可以通过熟悉操作来达到快速上手,更多的功能体验可以等到正式发布的时候去进行拓展

安装微信开发工具
下载网址

目录结构

app-
---image
---utils
---pages
---app.json

首先必备的就是app.json文件,这是小程序的配置文件,里面配置了路由,底部菜单,窗口标题等等一些最重要的东西

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "微信小练习",
    "navigationBarTextStyle":"black"
  },
  "tabBar": {
    "backgroundColor":"#000000",
    "list": [{
      "iconPath":"image/wechat.png",
      "selectedIconPath":"image/wechatHL.png",
      "pagePath": "pages/index/index",
      "text": "首页"
    }, {
      "iconPath":"image/record.png",
      "selectedIconPath":"image/play.png",
      "pagePath": "pages/about/about",
      "text": "我的"
    }]
  },
  "debug" : true
}

这个文件的内容可读性也是很不错,各个项目是做什么的让人一目了然, pages块的就是注册前端路由了,比如pages/index/index就是需要引用 pages/index 目录下的 index.wxml index.js index.wxss 我们不需要指定后缀,这都是自动完成的

其他的关于引入文件路径的问题都和这个是一类的

接下来就是关于页面的布局
pages/index目录下创建 index.wxml文件

<!--文章列表模板 begin-->
<template name="itmes">
    <view class="imgs"><image src="{{imgURL}}" class="in-img" background-size="cover" model="scaleToFill"></image></view>
    <view class="infos">
        <view class="title">{{title}}</view>
        <view class="date">{{time}}</view>
        <view class="classification">{{classification}}</view>
    </view>
    <view>hello world</view>
</template>
<!--文章列表模板 begin-->


<block wx:for="{{newList}}">
    <template is="itmes" data="{{...item}}" />
</block>

在这里, <template>这标签块内的东西只是一个模板,而不会实际显示出来,真正显示的是下面 <block>这个标签对里的东西,他在这里调用了上面的 template 模板, 当然此时是没有东西的,因为我们指定的变量还没有建立

赶紧在同级目录下创建 index.js

//获取应用实例
var app = getApp()
Page({
    data: {
        newList:[{url:"baidu.com",title:"111",classification:"ss",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"222",classification:"ss",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"333",classification:"12",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"444",classification:"333",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"555",classification:"44",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"666",classification:"44",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"777",classification:"32",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"888",classification:"123",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"999",classification:"456",time:"2016-10-17",imgURL:"../../image/test.png"},
            {url:"baidu.com",title:"000",classification:"454",time:"2016-10-17",imgURL:"../../image/test.png"}
        ]
    },
})

这个时候重新解析一下调试界面就能看到效果了,这个时候我们还没用到 .wxss 的文件,所以样式还是裸奔状态

larvel ORM模型中的一些方法和变量的作用

用代码说话

相关命令:

创建迁移文件

php artisan make:migration Pate_table

进行迁移

php artisan migrate

创建模型文件

php artisan make:model Page

把如下文件内容复制到对应的文件下,就可以按照注释进行测试了。控制器的代码就需要自己动手一遍了,用一次就会了

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Page extends Model
{
    //可批量注入的字段
    protected $fillable = ['*'];
    //不可批量注入的字段
    protected $guarded = [];

    //声明时间字段,这些字段可以调用Carbon,dateTime实例
    //如:
    //$page = Page::find(1);
    //dd($page->created_at->getTimestamp());
    protected $dates = ['created_at', 'updated_at'];

    //设置时间字段属性
//    protected  $dateFormat = 'U';

    //当使用 toArray() 和 toJson() 时要隐藏的字段
    protected $hidden = ['body'];

    //$hidden是隐藏 它是显示 两者存在一个就好
//    protected $visible = ['first_name', 'last_name'];


    //定义字段调用时的所用的格式
    //比如下面的$page->body 将自动json转array 存储的时候array自动转json
    protected $casts = [
        'body' => 'array',
    ];


    //append添加字段是当需要对数据添加不存在的字段的时候,在这里声明一下,
    //需要配合 getFooAttribute 方法使用,
    //注意:不要在这里写可调用的字段
    // 比如下面的 getTitleAttribute 这个,
    //如果 $append 数组中存在 title的话则会因为获取不到 $value 参数去执行方法内容 且会将结果覆盖到原有的字段
    protected $appends = ['other'];


    //当类似$page->title的时候调用该方法,
    //已发现的问题是在使用select foo as title的时候也会调用这个方法
    public function getTitleAttribute($value)
    {
        return strtolower($value).'+salt';
    }

    public function getOtherAttribute()
    {
        return 'this is other ziduan';
    }


    //当添加或更新title字段的时候将调用该方法,
    //例如$page->title = 'foo'; foo将作为参数传递到这里,
    public function setTitleAttribute($value)
    {
        $this->attributes['title'] = strtoupper($value);
    }
}

相关的表结构如下:

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('pages', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->string('slug')->nullable();
            $table->text('body')->nullable();
            $table->integer('user_id');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('pages');
    }
}

webuploader插件

通过这个插件让我感觉到大百度还仍然是技术公司
说了句废话,开始正题.
这里用的是插件提供的源码 插件官网传送门

demo就是拿的源码的 image-upload 示例,就是把代码中我们需要了解的给摘了出来

html代码

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <title>WebUploader演示</title>
    <link rel="stylesheet" type="text/css" href="{{ asset('webuploaderDemo/css/webuploader.css') }}" />
    <link rel="stylesheet" type="text/css" href="{{ asset('webuploaderDemo/examples/image-upload/style.css') }}" />
</head>
<body>
<div id="wrapper">
    <h1 id="stopBtn">暂停</h1>
    <h1 id="continueBtn">继续</h1>
    <h1 id="cancleBtn" attr="">取消</h1>
    <div id="container">
        <!--头部,相册选择和格式选择-->

        <div id="uploader">
            <div class="queueList">
                <div id="dndArea" class="placeholder">
                    <div id="filePicker"></div>
                    <p>或将文件拖动到这里,支持格式:mp4,rmvb,上限3个</p>
                </div>
            </div>
            <div class="statusBar" style="display:none;">
                <div class="progress">
                    <span class="text">0%</span>
                    <span class="percentage"></span>
                </div><div class="info"></div>
                <div class="btns">
                    <div id="filePicker2"></div><div class="uploadBtn">暂停</div>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/javascript" src="{{ asset('webuploaderDemo/examples/image-upload/jquery.js') }}"></script>
<script type="text/javascript" src="{{ asset('webuploaderDemo/dist/webuploader.js') }}"></script>
<script type="text/javascript" src="{{ asset('webuploaderDemo/examples/image-upload/upload3.js') }}"></script>
</body>
</html>

js部分

(function( $ ){
    // 当domReady的时候开始初始化
    $(function() {
        var $wrap = $('#uploader'),

            // 图片容器
            $queue = $( '<ul class="filelist"></ul>' )
                .appendTo( $wrap.find( '.queueList' ) ),

            // 状态栏,包括进度和控制按钮
            $statusBar = $wrap.find( '.statusBar' ),

            // 文件总体选择信息。
            $info = $statusBar.find( '.info' ),

            // 上传按钮
            $upload = $wrap.find( '.uploadBtn' ),

            // 没选择文件之前的内容。
            $placeHolder = $wrap.find( '.placeholder' ),

            $progress = $statusBar.find( '.progress' ).hide(),

            // 添加的文件数量
            fileCount = 0,

            // 添加的文件总大小
            fileSize = 0,

            // 优化retina, 在retina下这个值是2
            ratio = window.devicePixelRatio || 1,

            // 缩略图大小
            thumbnailWidth = 110 * ratio,
            thumbnailHeight = 110 * ratio,

            // 可能有pedding, ready, uploading, confirm, done.
            state = 'pedding',

            // 所有文件的进度信息,key为file id
            percentages = {},
            // 判断浏览器是否支持图片的base64
            isSupportBase64 = ( function() {
                var data = new Image();
                var support = true;
                data.onload = data.onerror = function() {
                    if( this.width != 1 || this.height != 1 ) {
                        support = false;
                    }
                }
                data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
                return support;
            } )(),

            // 检测是否已经安装flash,检测flash的版本
            flashVersion = ( function() {
                var version;

                try {
                    version = navigator.plugins[ 'Shockwave Flash' ];
                    version = version.description;
                } catch ( ex ) {
                    try {
                        version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash')
                                .GetVariable('$version');
                    } catch ( ex2 ) {
                        version = '0.0';
                    }
                }
                version = version.match( /\d+/g );
                return parseFloat( version[ 0 ] + '.' + version[ 1 ], 10 );
            } )(),

            supportTransition = (function(){
                var s = document.createElement('p').style,
                    r = 'transition' in s ||
                            'WebkitTransition' in s ||
                            'MozTransition' in s ||
                            'msTransition' in s ||
                            'OTransition' in s;
                s = null;
                return r;
            })(),

            // WebUploader实例
            uploader;

        if ( !WebUploader.Uploader.support('flash') && WebUploader.browser.ie ) {

            // flash 安装了但是版本过低。
            if (flashVersion) {
                (function(container) {
                    window['expressinstallcallback'] = function( state ) {
                        switch(state) {
                            case 'Download.Cancelled':
                                alert('您取消了更新!')
                                break;

                            case 'Download.Failed':
                                alert('安装失败')
                                break;

                            default:
                                alert('安装已成功,请刷新!');
                                break;
                        }
                        delete window['expressinstallcallback'];
                    };

                    var swf = './expressInstall.swf';
                    // insert flash object
                    var html = '<object type="application/' +
                            'x-shockwave-flash" data="' +  swf + '" ';

                    if (WebUploader.browser.ie) {
                        html += 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ';
                    }

                    html += 'width="100%" height="100%" style="outline:0">'  +
                        '<param name="movie" value="' + swf + '" />' +
                        '<param name="wmode" value="transparent" />' +
                        '<param name="allowscriptaccess" value="always" />' +
                    '</object>';

                    container.html(html);

                })($wrap);

            // 压根就没有安转。
            } else {
                $wrap.html('<a href="http://www.adobe.com/go/getflashplayer" target="_blank" border="0"><img alt="get flash player" src="http://www.adobe.com/macromedia/style_guide/images/160x41_Get_Flash_Player.jpg" /></a>');
            }

            return;
        } else if (!WebUploader.Uploader.support()) {
            alert( 'Web Uploader 不支持您的浏览器!');
            return;
        }

        // 实例化
        uploader = WebUploader.create({
            auto: true,         //开启自动上传
            pick: {             //点击选择文件按钮
                id: '#filePicker',
                label: '点击选择文件',
                multiple : false //是否开起同时选择多个文件能力。
            },
            formData: {         //上传时的文本内容
                uid: 5173180,
                filename : 'filename_test'
            },
            dnd: '#dndArea',                    //文件可拖拽范围
            paste: '#uploader',                 //可通过粘贴添加到文件框内
            swf: '../../dist/Uploader.swf',     //flash文件
            chunked: true,                      //开启分片上传
            chunkSize: 512 * 1024,      //分片大小 512k
            server : 'http://testlaravel/web/uploader', //后端处理函数
            // runtimeOrder: 'flash',

             accept: {      //限制文件类型
                 title: 'Video',
                 extensions: 'mp4,rmvb',
                 mimeTypes: 'video/*'
             },

            disableGlobalDnd: true,             // 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。
            fileNumLimit: 1,                    //限制上传一个文件
            fileSizeLimit: 1024 * 1024 * 1024,  // 1 G
            fileSingleSizeLimit: 1024 * 1024 * 1024,    // 单个最大文件 1G
            method : 'POST'                     //指定上传方法
        });


        //// 拖拽时不接受 js, txt 文件。
        uploader.on( 'dndAccept', function( items ) {
            var denied = false,
                len = items.length,
                i = 0,
                // 修改js类型
                unAllowed = 'text/plain;application/javascript ';

            for ( ; i < len; i++ ) {
                // 如果在列表里面
                if ( ~unAllowed.indexOf( items[ i ].type ) ) {
                    denied = true;
                    break;
                }
            }

            return !denied;
        });


        var oldTime = new Date().getTime();

        uploader.on( 'uploadProgress', function( file, percentage ) {
            console.log('上传进度:'+ percentage * 100 + '%');

            var currentTime = new Date().getTime();
            var cha = currentTime - oldTime;

            var chunkSize = 512;

            //设置网速
            var ws = (chunkSize/cha)*1000;


            console.log('当前网速:' + ws + 'kb/s');

            oldTime = currentTime;

        });

        //接收服务器返回信息
        //这里才是接收服务器信息的地方,如果有出错就 `return false` 来停止上传
        uploader.on( 'uploadAccept', function( file, response ) {
            var str = response._raw;
            var info = eval('(' + str + ')');
            console.log(info);
            //if ( hasError ) {
            //    // 通过return false来告诉组件,此文件上传有错。
            //    return false;
            //}
        });

        uploader.onFileQueued = function( file ) {
            console.log('添加文件动作');
            $('#cancleBtn').attr({'attr': file.id});
        };

        uploader.onUploadFinished = function(){
            console.log('完成上传');
        }

        $('#stopBtn').on( 'click', function() {
            console.log("暂停上传...");
            uploader.stop(true);
        });

        $('#continueBtn').on( 'click', function() {
            console.log("继续");
            uploader.upload();
        });

        $('#cancleBtn').on( 'click', function() {
            console.log("取消上传");
            var id = $(this).attr('attr');
            //指定取消的文件id
            uploader.cancelFile(id);
        });


    });
})( jQuery );

后端php部分

public function testSaveWebUploader(Request $request){
        //强制浏览器不缓存
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
        header("Cache-Control: no-store, no-cache, must-revalidate");
        header("Cache-Control: post-check=0, pre-check=0", false);
        header("Pragma: no-cache");


        //指定提交方式
        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            exit; // finish preflight CORS requests here
        }

        //当出现debug判断服务器报错
        if ( !empty($_REQUEST[ 'debug' ]) ) {
            $random = rand(0, intval($_REQUEST[ 'debug' ]) );
            if ( $random === 0 ) {
                header("HTTP/1.0 500 Internal Server Error");
                exit;
            }
        }


        @set_time_limit(5 * 60);


        //临时文件夹和存放文件的文件夹
        $targetDir = 'upload_tmp';
        $uploadDir = 'upload';

        $cleanupTargetDir = true; // Remove old files
        $maxFileAge = 5 * 3600; // Temp file age in seconds



        // 检测文件夹是否存在
        if (!file_exists($targetDir)) {
            @mkdir($targetDir);
        }

        if (!file_exists($uploadDir)) {
            @mkdir($uploadDir);
        }


        // 重新设置文件名
        $fileName = $this->renameFile();

        $filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
        $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;

        // 是否分片
        $chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
        $chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;


        // 删除过期文件
        if ($cleanupTargetDir) {
            if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
            }

            while (($file = readdir($dir)) !== false) {
                $tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;

                // 忽略当前临时文件
                if ($tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp") {
                    continue;
                }

                // 过期文件删除
                if (preg_match('/\.(part|parttmp)$/', $file) && (@filemtime($tmpfilePath) < time() - $maxFileAge)) {
                    @unlink($tmpfilePath);
                }
            }
            closedir($dir);
        }


        // 打开缓存文件
        if (!$out = @fopen("{$filePath}_{$chunk}.parttmp", "wb")) {
            die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
        }

        if (!empty($_FILES)) {
            if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
            }

            // Read binary input stream and append it to temp file
            if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        } else {
            if (!$in = @fopen("php://input", "rb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
            }
        }

        while ($buff = fread($in, 4096)) {
            fwrite($out, $buff);
        }

        @fclose($out);
        @fclose($in);

        //重命名文件名
        rename("{$filePath}_{$chunk}.parttmp", "{$filePath}_{$chunk}.part");

        $index = 0;
        $done = true;
        for( $index = 0; $index < $chunks; $index++ ) {
            if ( !file_exists("{$filePath}_{$index}.part") ) {
                $done = false;
                break;
            }
        }
        if ( $done ) {
            if (!$out = @fopen($uploadPath, "wb")) {
                die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
            }

            if ( flock($out, LOCK_EX) ) {
                for( $index = 0; $index < $chunks; $index++ ) {
                    if (!$in = @fopen("{$filePath}_{$index}.part", "rb")) {
                        break;
                    }

                    while ($buff = fread($in, 4096)) {
                        fwrite($out, $buff);
                    }

                    @fclose($in);
                    @unlink("{$filePath}_{$index}.part");
                }

                flock($out, LOCK_UN);
            }
            @fclose($out);
            die("{'jsonrpc' : '2.0', 'result' : 'done', 'chunk' : '{$chunk}', 'chunks': '{$chunks}'}");
        }

        // Return Success JSON-RPC response
        die("{'jsonrpc' : '2.0', 'result' : 'success', 'chunk' : '{$chunk}', 'chunks': '{$chunks}'}");

    }

    protected function renameFile(){
        //获取后缀
        $ext = pathinfo($_FILES["file"]["name"], PATHINFO_EXTENSION);
        //文件名
        if (isset($_REQUEST["name"])) {
            $fileName = isset($_REQUEST["filename"]) ? $_REQUEST["filename"] : $_REQUEST["name"];
        } elseif (!empty($_FILES)) {
            $fileName = $_FILES["file"]["name"];
        } else {
            $fileName = uniqid("file_");
        }

        $key = isset($_REQUEST['uid']) ? $_REQUEST['uid'] : 'hotcast';

        $fileName = md5($fileName.$key);

        return $fileName.'.'.$ext;
    }

最后

相关的代码已经贴出来了,事件调用在js部分找,后端处理的大体思路就是保存分片上传过来的文件,当文件上传完毕后就通过fwrite或者file_put_content这类的函数给按顺序怼到一个文件当中
//如果真的懒需要源码的话 我这有 , 写在了laravel框架里面了,但是代码可以单独拿出来,依赖性并不高