1. 程式人生 > >Swoole一鍵操作基於阿里雲的RDS資料庫遷移+OSS檔案搬遷

Swoole一鍵操作基於阿里雲的RDS資料庫遷移+OSS檔案搬遷

    傳統的資料庫搬遷思路是把資料庫表的結構及資料都查詢出來,然後通過迴圈進行資料結構重組拼接。然後匯出!資料量少的話,這樣當然是沒毛病。當資料量太大的時候,伺服器的記憶體開銷就吃不住了,很容易炸掉,導致伺服器癱掉。當然我之前也這麼幹的 ,所以也一直想辦法解決這個問題  ,當你體會到大資料搬遷的那酸爽、提心吊膽你就明白了  

    一天我看到一條sql  

SELECT COUNT( * ) as cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA = 舊資料庫名

CREATE TABLE IF NOT EXISTS  新資料庫名.表名 LIKE 舊資料庫名.表名

INSERT 新資料庫名.表名  SELECT * FROM 舊資料庫名.表名

相信看到這幾條sql 就應該有思路了,沒錯就是這三條sql!

廢話不說了  上程式碼

<?php  

namespace action\swoole;

/**

* 換域名

*/

class changesiteAction extends \Action{

    private $bucket = 'oss';

    private $oldPrefix = '';

    private $newPrefix = '';

    private $maxkeys = 1000;

    private $initDB = array('aaa',bbb',ccc','ccc'); //多分支資料庫


    private $upgrading = array(

        'tag' => 'copying',

        'room' => '1');

    

    private    $config = array(

        'demo_action_admin_certificateconfig',

        'demo_action_admin_dealerentranceconfig',

        'demo_action_cash_config',

        'demo_action_customer_config',

        'demo_action_dealer_config',

        'demo_action_delivery_config',

        'demo_action_itemcomm_config',

        'demo_action_poster_config',

        'demo_action_seckill_config',

        'demo_action_shop_config',

        'demo_action_user_config',

        'demo_action_admin_skinconfig',

        'demo_action_wechat_config',

        'demo_action_xiaoi_config',

        'demo_action_yunpay_config',

        'demo_model_item_config',

        'demo_sysapi_ultraconfig',

        'demo_sysapi_payconfig'

    );


    //重新連線

    public function reload(){

        if($this->linker->server->reload()) $message = array('msgcode'=>'RELOAD');

        else $message = array('msgcode'=>'FAILURE');

        PUSH($this->linker,$message);

    }

    //心跳連線

    public function heartbeat() {

        $res = $this->linker->messaging;

        $roomid = $res['roomid'];

        PUSH($this->linker,array('errcode'=>'heartbeat'),'adventure_'.$roomid);

        return false;

    }

    public function init(){

        // 此時已經可以進行通訊!

        PUSH($this->linker,$this->upgrading);

        return $this->upgrading;

    }

    //第一步

    public function copyStart(){

        $arg = $this->linker->messaging;

        if(empty($arg['newsite']))   return PUSH($this->linker,array('errcode'=>'ERR_POST_NEWSITE','errmsg'=>'新域名為空'));

        if(empty($arg['oldsite']))   return PUSH($this->linker,array('errcode'=>'ERR_POST_OLDSITE','errmsg'=>'舊域名為空'));

        $newsite=strtolower(trim($arg['newsite']));

        $oldsite=strtolower(trim($arg['oldsite']));

        $m = $this->m(DBMATRIX);

        //檢測舊域名是否存在

        $site = $m->sel()->from('customer')->where(array("domain = ",$oldsite))->exe();

        if(empty($site)){

            PUSH($this->linker,array('errcode'=>'ERR_EXIST_DOMAIN','errmsg'=>'舊域名不存在'));

            return false;

        }


        // 檢查新域名是否被使用

        $mysql = "select id from demo_customer where domain = '{$newsite}'";

        $myres = $m->exe(false,$mysql);

        if(!empty($myres)){

            PUSH($this->linker,array('errcode'=>'DOMAIN_HAS_USE','errmsg'=>'新域名已使用過'));

            return false;

        }

        //查詢資料庫表的數量

        $oldDB = preg_replace("/[\W]/", "_", $oldsite);

        $sql = "SELECT COUNT( * ) as cnt FROM information_schema.TABLES WHERE TABLE_SCHEMA = '{$oldDB}'";

        $tables_num  = $this->m($this->initDB[$site['branch']])->exe(false,$sql)['cnt'];

        //新增域名修改記錄表

        $mysql="select * from demo_changesite_log where newsite='{$newsite}' and oldsite = '{$oldsite}'";

        $logres=$this->m(DBMATRIX)->exe(false,$mysql);

        if(empty($logres)){

            $data['sql_sum'] = $tables_num;

            $data['siteid']=$site['id'];

            $data['newsite']=$newsite;

            $data['oldsite']=$oldsite;

            $data['step'] = 1;

            //$data['admid'] = $_SESSION['admid'];

            $res=$this->m(DBMATRIX)->ins('changesite_log')->values($data)->exe();

            if(empty($res)){

                PUSH($this->linker,array('errcode'=>'ERR_INS_LOG','errmsg'=>'修改記錄新增失敗'));

                return false;

            }

            return PUSH($this->linker,array('errcode'=>'FINISHREADY','id'=>$res,'numsql'=>$tables_num,'step'=>1));

        }

        return PUSH($this->linker,array('errcode'=>'FINISHREADY','id'=>$logres['id'],'numsql'=>$tables_num,'step'=>$logres['step']));

        

    }

    //第二步

    public function copyData(){

        $arg = $this->linker->messaging;

        if(empty($arg['id']))   return PUSH($this->linker,array('errcode'=>'ERR_POST_ID','errmsg'=>'ID為空'));

        $log = $this->m(DBMATRIX)->sel('oldsite,newsite')->from('changesite_log')->where(array('id =',$arg['id']))->exe();dump($log);

        if(empty($log))   return PUSH($this->linker,array('errcode'=>'ERR_LOG','errmsg'=>'記錄不存在','id'=>$arg['id']));

        $newsite=strtolower(trim($log['newsite']));

        $oldsite=strtolower(trim($log['oldsite']));

        $oldDB = preg_replace("/[\W]/", "_", $oldsite);

        $newDB = preg_replace("/[\W]/", "_", $newsite);


        $site=$this->m(DBMATRIX)->sel('id,branch')->from('customer')->where(array('domain=',$oldsite))->exe();dump($site);

        

        //切換資料庫

        $sql='use `'.$newDB.'`';

        $res=$this->m($this->initDB[$site['branch']])->exe(false,$sql);

        if(!$res){

             //建立新資料庫

             $sql='create database `'.$newDB.'`';

             $this->m($this->initDB[$site['branch']])->exe(false,$sql);

             $sql='use `'.$newDB.'`';

             $res = $this->m($this->initDB[$site['branch']])->exe(false,$sql);

            if(!empty($res))  return PUSH($this->linker,array('errcode'=>'ERR_DATABASW','errmsg'=>'資料庫建立失敗'));

        }

        /**

         * 這裡直接進行表結構及資料的複製,直接省去由於害怕資料丟失的備份

         * CREATE TABLE admin_jingzhunfenxiao_com.admin2 LIKE 3n4w_jingzhunfenxiao_com.demo_admin;    複製表結構

         * INSERT admin_jingzhunfenxiao_com.admin2 SELECT * FROM 3n4w_jingzhunfenxiao_com.demo_admin; 複製表資料

         *  array(286) {

                  [0] =&gt; array(1) {

                    [&quot;Tables_in_zzz_zzz_com&quot;] =&gt; string(15) &quot;demo_agent_item&quot;

                  }

                  [1] =&gt; array(1) {

                    [&quot;Tables_in_zzz_zzz_com&quot;] =&gt; string(17) &quot;demo_agent_record&quot;

                  }

                  [2] =&gt; array(1) {

                    [&quot;Tables_in_zzz_zzz_com&quot;] =&gt; string(15) &quot;demo_agent_spec&quot;

                  }

                }

        **/


        $sql = "show tables";

        $tables = $this->m($oldsite)->exe(true,$sql);

        $numsql = 0;$errsql = '';

        if(!empty($tables)){

            $m = $this->m($oldsite);

            foreach ($tables as $k => $va) {

                $numsql = $k;

                $tablename = $va['Tables_in_'.$oldDB];

                $ssql = "show tables {$tablename}";

                $tab = $m->exe(false,$ssql);

                if(empty($tab)){

                    $tsql = "CREATE TABLE IF NOT EXISTS  {$newDB}.{$tablename} LIKE {$oldDB}.{$tablename}";

                    $tres = $m->exe(false,$tsql);

                    if(empty($tres)){

                        $errsql = $k.'-1';break;

                    }   

                    $dsql = "INSERT {$newDB}.{$tablename} SELECT * FROM {$oldDB}.{$tablename}";

                    $dres = $m->exe(false,$dsql);

                }

                

                PUSH($this->linker,array('errcode'=>'SUCCESS_CREATE','num'=>$numsql+1));

            }

            //有錯誤導致建立沒有繼續執行,記錄錯誤位置

            if(!empty($errsql)){

                $mysql="update demo_changesite_log set step=2 , sqlid={$errsql}  where id={$arg['id']}";

                $myres=$this->m(DBMATRIX)->exe(false,$mysql);echo $mysql;die;

                return PUSH($this->linker,array('errcode'=>'ERR_CREATE','errmsg'=>'資料斷開連線'));

            }

        }

        $mysql="update demo_changesite_log set step=2 , sqlid={$numsql}  where id={$arg['id']}";

        $myres=$this->m(DBMATRIX)->exe(false,$mysql);

        return  PUSH($this->linker,array('errcode'=>'FINISHCOPYDATA','errmsg'=>'複製成功'));

    }

    //第三步   複製oss檔案

    // SOCKET 模式搬遷

    public function startSocketCopy(){

        $arg = $this->linker->messaging;

        if(empty($arg['id']))   return PUSH($this->linker,array('errcode'=>'ERR_POST_ID','errmsg'=>'ID為空'));

        $log = $this->m(DBMATRIX)->sel('oldsite,newsite')->from('changesite_log')->where(array('id =',$arg['id']))->exe();

        if(empty($log))   return PUSH($this->linker,array('errcode'=>'ERR_LOG','errmsg'=>'記錄不存在'));

        $newsite=strtolower(trim($log['newsite']));

        $oldsite=strtolower(trim($log['oldsite']));

        

        $this->oldPrefix = $oldsite.'/';

        $this->newPrefix = $newsite.'/';

        

        $this->copyInSocket($this->fullTheList($this->oldPrefix));

        

        $mysql="update demo_changesite_log set step=3 where id={$arg['id']}";

        $myres=$this->m(DBMATRIX)->exe(false,$mysql);

        return PUSH($this->linker,array('errcode'=>'FINISHCOPYOSS','errmsg'=>'OSS檔案搬遷完成'));

    }

    //第四步  修改總站配置及刪除舊庫

    public function alterConfig(){

        $arg = $this->linker->messaging;

        if(empty($arg['id']))   return PUSH($this->linker,array('errcode'=>'ERR_POST_ID','errmsg'=>'ID為空'));

        $log = $this->m(DBMATRIX)->sel('oldsite,newsite')->from('changesite_log')->where(array('id =',$arg['id']))->exe();

        if(empty($log))   return PUSH($this->linker,array('errcode'=>'ERR_LOG','errmsg'=>'記錄不存在'));

        $newsite=strtolower(trim($log['newsite']));

        $oldsite=strtolower(trim($log['oldsite']));


        $m=$this->m(DBMATRIX);

        //搬總站配置

        for($i=0;!empty($this->config[$i]);$i++){

             // 找出新域名名配置

             $mysql = "select id,http_host from `".$this->config[$i]."` where `http_host` = '{$newsite}'";

             $config_from = $m->exe(false,$mysql);

             if($config_from)  continue;

             // 找出原域名配置

             $mysql = "select id,http_host from `".$this->config[$i]."` where `http_host` = '{$oldsite}'";

             $config_from = $m->exe(false,$mysql);

             if(!$config_from){

                $mysql = "insert into  `".$this->config[$i]."`(http_Host) values('{$newsite}') ";

                $myre = $m->exe(false,$mysql);

                if(!$myre)  return PUSH($this->linker,array('errcode'=>'ERR_CONFIG','errmsg'=>'資料斷開連線'));

             }else{

                // 新域名配置插入表

                 $mysql = "update `".$this->config[$i]."` set http_host='{$newsite}' where http_host = '{$oldsite}'";

                 $myre = $this->m(DBMATRIX)->exe(false,$mysql);

                 if(!$myre)   return PUSH($this->linker,array('errcode'=>'ERR_CONFIG','errmsg'=>'資料斷開連線'));

             }

             

        }

        //修改customer

        $site = $m->sel('id,version,branch')->from('customer')->where(array('domain=',$newsite))->exe();

        if(!$site){

            $site = $m->sel('id,version,branch')->from('customer')->where(array('domain=',$oldsite))->exe();

            $res = $m->upd('customer')->set(array('domain'=>$newsite))->where(array('id=',$site['id']))->exe();

            if(!$res)   return PUSH($this->linker,array('errcode'=>'ERR_CUSTOMER','errmsg'=>'資料斷開連線'));

        }

        //把新域名寫入域名修改表(customer_update)

        $list=$m->sel()->from('customer_update')->where(array('siteid=',$site['id']," and domain=",$newsite))->exe();

        if(!$list){

            $data = date("Y-m-d H:i:s");

            $sql="insert into demo_customer_update (siteid,domain,time) values({$site['id']},'{$newsite}','{$data}')";

            $log=$m->exe(false,$sql);

            if(!$log)  return PUSH($this->linker,array('errcode'=>'ERR_INSERT','errmsg'=>'資料斷開連線'));

        }

        //刪除舊資料庫

        $database = preg_replace("/[\W]/", "_", $oldsite);

        $sql='DROP DATABASE `'.$database.'`';       

        //$res= $this->m($this->initDB[$site['branch']])->exe(false,$sql);

        //if(!$res)  return PUSH($this->linker,array('errcode'=>'ERR_DROP_DATABASE','errmsg'=>'資料斷開連線'));


        $mysql="update demo_changesite_log set step=6 ,state=1  where id={$arg['id']}";

        $res=$m->exe(false,$mysql);


        return PUSH($this->linker,array('errcode'=>'FINISHTASK','errmsg'=>'任務完成'));

    }


    private function fullTheList($Prefix){

        

        $ObjectListInfo = A($this->linker,'oss/object/listObjects',array($this->bucket,array('max-keys'=>$this->maxkeys,'prefix'=>$Prefix)));

        $ObjectList = $subObjectList = $ObjectListInfo->getObjectList();

        $PrefixList = $subPrefixList = $ObjectListInfo->getPrefixList();

        

        /*

            每次只能獲取1000條

            迴圈拼接第1000條後面的檔案或資料夾到陣列中

        */

        while(true){

            

            $numObject = count($subObjectList);

            $numPrefix = count($subPrefixList);

            if($numObject+$numPrefix == 0) break;

            if($numObject+$numPrefix < $this->maxkeys) break;

            

            /*

                只有檔案時,取檔案

                只有資料夾時,取資料夾

                都有時,取較大值

            */

            while(true){

                

                if(!$numObject) {$lastObject = $subPrefixList[$numPrefix-1]->getPrefix();break;}

                if(!$numPrefix) {$lastObject = $subObjectList[$numObject-1]->getKey();break;}

                

                $Key = $subObjectList[$numPrefix-1]->getKey();

                $Prefix = $subPrefixList[$numObject-1]->getPrefix();

                $lastObject = strcmp($Key,$Prefix)>0?$Key:$Prefix;

                break;

            }

            

            $subObjectListInfo = A($this->linker,'oss/object/listObjects',array($this->bucket,array('max-keys'=>$this->maxkeys,'prefix'=>$Prefix,'marker'=>$lastObject)));

            $subObjectList = $subObjectListInfo->getObjectList();

            $subPrefixList = $subObjectListInfo->getPrefixList();

            

            $ObjectList = array_merge($ObjectList,$subObjectList);

            $PrefixList = array_merge($PrefixList,$subPrefixList);

        }

        

        return array('ObjectList'=>$ObjectList,'PrefixList'=>$PrefixList);

    }

    

    /*

        遞迴函式:傳入完整的檔案與資料夾列表

        引數:array(

            'ObjectList'=>$ObjectList,

            'PrefixList'=>$PrefixList

        );

    */

    private function copyInSocket($the){

        

        foreach($the['ObjectList'] as $obj){

            

            $key = $obj->getKey();

            if(preg_match('/\+/',$key)) continue;

            $newKey = $this->newKey($this->oldPrefix,$this->newPrefix,$key);

            $res = A($this->linker,'oss/object/copyObject',array($this->bucket,$key,$this->bucket,$newKey));

            PUSH($this->linker,array('errcode'=>'COPYOBJECT'));

        }

        

        foreach($the['PrefixList'] as $pre) $this->copyInSocket($this->fullTheList($pre->getPrefix()));

    }

    

    private function newKey($oldPrefix,$newPrefix,$obj){

        

        return preg_replace('/^'.str_replace('/','\/',$oldPrefix).'/',$newPrefix,$obj);

    }


}

前端程式碼

html

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8">

    <title>換域名</title>

    <meta name="renderer" content="webkit">

    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

    <link rel="stylesheet" href="http://3n4w.oss-cn-shenzhen.aliyuncs.com/public/client-resource/layui-master/dist/css/layui.css">

    <link rel="stylesheet" type="text/css" href="/admin/public/common.css">

    <link rel="stylesheet" type="text/css" href="/admin/public/reset.css"/>

    <link rel="stylesheet" type="text/css" href="domain.css"/>

    <!-- 注意:如果你直接複製所有程式碼到本地,上述css路徑需要改成你本地的 -->

</head>

<body >

    <blockquote class="layui-elem-quote">

             <span class="title">域名更換操作</span>

    </blockquote>

    <div class="content">

        <div class="layui-inline">

            <div class="layui-form-item">

                <label class="layui-form-label">舊域名</label>

                <div class="layui-input-inline">

                      <input type="text" name="username" lay-verify="required" placeholder="請輸入" autocomplete="off" class="layui-input" id="oldsite">

                </div>

              </div>

            <div class="layui-form-item">

                <label class="layui-form-label">新域名</label>

                <div class="layui-input-inline">

                      <input type="text" name="username" lay-verify="required" placeholder="請輸入" autocomplete="off" class="layui-input" id="newsite">

                </div>

              </div>

        </div>

        <div class="layui-inline">

            <button class="layui-btn save-btn fl" id="save-btn">儲存</button>

            <button class="layui-btn reload fl" id="reload">重啟</button>

        </div>

    </div>

    

    

    <div class="progress" id="progress">

        <fieldset class="layui-elem-field layui-field-title" style="margin-top: 30px;">

            <legend>操作進度</legend>

        </fieldset>

        <ul class="layui-timeline">

            <li class="layui-timeline-item">

                <i class="layui-icon layui-timeline-axis"></i>

                <div class="layui-timeline-content layui-text">

                    <div class="layui-timeline-title"><label class="step1">換域名準備階段</label><label class="state st1"></label></div>

                </div>

            </li>

            <li class="layui-timeline-item">

                <i class="layui-icon layui-timeline-axis">  </i>

                <div class="layui-timeline-content layui-text">

                    <div class="layui-timeline-title"><label class=" step2">資料庫表資料複製</label><label class="state st2"></label></div>

                </div>

            </li>

            <li class="layui-timeline-item">

                <i class="layui-icon layui-timeline-axis"></i>

                <div class="layui-timeline-content layui-text">

                    <div class="layui-timeline-title"><label class="step3">OSS儲存檔案複製</label><label class="state st3"></label></div>

                </div>

            </li>

            <li class="layui-timeline-item">

                <i class="layui-icon layui-timeline-axis"></i>

                <div class="layui-timeline-content layui-text">

                    <div class="layui-timeline-title"><label class="step4">配置檔案修改</label><label class="state st4"></label></div>

                </div>

            </li>

            <li class="layui-timeline-item">

                <i class="layui-icon layui-timeline-axis"></i>

                <div class="layui-timeline-content layui-text">

                    <div class="layui-timeline-title"><label class="step5">操作已完成</label></div>

                </div>

            </li>

        </ul>

    </div>

    

                                

                        

    <script type="text/javascript"  src="http://3n4w.oss-cn-shenzhen.aliyuncs.com/public/client-resource/layui-master/dist/layui.js"></script>

    <!-- 注意:如果你直接複製所有程式碼到本地,上述js路徑需要改成你本地的 -->

    <script type="text/javascript"  src="domain.js"></script>

    <script>

    </script>


</body>

</html>

js

layui.use(['form'], function() {

    var form = layui.form,

        $ = layui.jquery;

    var websocket = new WebSocket('ws://192.168.1.1:9997?swoole=dev&mca=swoole/changesite/init&http_host=www.baidu.com');

    

    var save_btn   = document.getElementById("save-btn");

    var oldsite     = document.getElementById("oldsite");

    var newsite   = document.getElementById("newsite");

    var progress = document.getElementById("progress");

    var finishcopy = document.getElementById("finishcopy");

    var reload_btn = document.getElementById('reload');

    reload_btn.onclick = function(){

        var copyStart = {

            mca : 'swoole/changesite/reload'};

        websocket.send(JSON.stringify(copyStart));

    }



    var reload  = {

        mca : 'swoole/changesite/reload',

        arg : 'arg1',};

    var beating = {

        mca : 'swoole/changesite/beating',

        arg : 'arg1',};

    

    var copyNum = 0;var id = 0;var step =0;


    var copyReady = function(num){console.log(num);

        $('.step1').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

        $('.st1').html('已完成');

        $('.st2').html("正在複製<b class='has-copy'>0</b> /<b class='tabnum'>"+num+" </b>");

        if(step==1){

            copyData();

        }

        if(step==2){

            startSocketCopy();

        }

        if(step==3){

            $('.step2').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

            $('.st2').html("已完成");

            alterConfig();

        }

        

    }

    var copyData = function(){

        var copyStart = {

            mca : 'swoole/changesite/copyData',

            id : id};

        websocket.send(JSON.stringify(copyStart));

    }

    var startSocketCopy = function(){

        $('.step2').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

        $('.st2').html("已完成");

        $('.st3').html("正在複製<b class='has-file'>0</b>");

        var copyStart = {

            mca : 'swoole/changesite/startSocketCopy',

            id : id};console.log(copyStart);

        websocket.send(JSON.stringify(copyStart));

    }

    var alterConfig = function(){

        $('.step3').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

        $('.st3').html("已完成");

        $('.st4').html("正在修改配置檔案");

        var copyStart = {

            mca : 'swoole/changesite/alterConfig',

            id : id};

        websocket.send(JSON.stringify(copyStart));

    }


    var taskEnd = function(){

        $('.step4').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

        $('.st4').html("已完成");

        $('.step5').addClass('has-end').parents('.layui-timeline-content').siblings('.layui-icon').addClass('has-over');

    }


    save_btn.onclick = function(){

        progress.style.display = 'block';

        

        var copyStart = {

            mca : 'swoole/changesite/copyStart',

            oldsite : oldsite.value,

            newsite : newsite.value};


        websocket.send(JSON.stringify(copyStart));

    }


    

    websocket.onopen = function (evt) {

        console.log("Connected to WebSocket server.");

        websocket.send(JSON.stringify(reload));

        window.setInterval(function(){websocket.send(JSON.stringify(beating));}, 10000);

    };


    websocket.onmessage = function (evt) {

        var that = this;

        var data = eval('('+evt.data+')');

        console.log(data);

        switch(data.message.errcode){

            case 'FINISHREADY':

                id = data.message.id;

                step = data.message.step;

                copyReady(data.message.numsql);

                break;

            case 'FINISHCOPYDATA':

                startSocketCopy();

                break;

            case 'FINISHCOPYOSS':

                alterConfig();

                break;

            case 'FINISHTASK':

                taskEnd();

                break;

            case 'SUCCESS_CREATE':

                $('.has-copy').html(data.message.num);console.log(data.message.num);

                break;

            case 'COPYOBJECT':

                $('.has-file').html(++copyNum);

                break;

            //default:console.log(data.message.errmsg);break;

        }

    };


    websocket.onclose = function (evt) {console.log("Disconnected");};

    websocket.onerror = function (evt, e) {console.log('Error occured: ');console.log(evt.data);};

});

css

.content {

    padding: 20px 80px;

}

button.layui-btn.save-btn.fl {

    margin-left: 100px;

}

.layui-timeline {

    padding-left: 100px;

}

.finish {

    background-color: #a53333;

}

.progress{

    display: none;

}

.state{

    color: #c74949;

    padding-left: 75px;

}

.has-over{

    background-color: #c74949;

}

.has-end{

    color: #c74949;

}