SpringBoot 仿抖音短視頻小程序開發(三)

SpringBoot 仿抖音短視頻小程序開發(一):項目的簡介( https://segmentfault.com/a/11...
SpringBoot 仿抖音短視頻小程序開發(二):項目功能分析與具體實現( https://segmentfault.com/a/11...
源代碼: SpringBoot 仿抖音短視頻小程序開發 全棧式實戰項目( https://gitee.com/scau_zns/sh...
短視頻後臺管理系統:( https://gitee.com/scau_zns/sh...

小程序的後臺管理系統

clipboard.png

涉及的技術棧:Bootstrap + jQuery + jGrid + SSM框架 + zookeeperjavascript

1、用戶列表的獲取與分頁

前端代碼:css

<div class="usersList_wrapper">
                <!-- 用戶列表展現的表格 -->  
                <table id="usersList"></table>
                <!-- 底部的分頁條 -->  
                <div id="usersListPager"></div>  
            </div>

jGrid發送請求獲取數據封裝好展現到頁面html

// 用戶列表
        var handleList = function() {
            
            // 上下文對象路徑
            var hdnContextPath = $("#hdnContextPath").val();
            var apiServer = $("#apiServer").val();
            
            var jqGrid = $("#usersList");  
            jqGrid.jqGrid({  
                caption: "短視頻用戶列表",  
                url: hdnContextPath + "/users/list.action",  
                mtype: "post",  
                styleUI: 'Bootstrap',//設置jqgrid的全局樣式爲bootstrap樣式  
                datatype: "json",  
                colNames: ['ID', '頭像', '用戶名', '暱稱', '粉絲數', '關注數', '獲贊數'],  
                colModel: [  
                    { name: 'id', index: 'id', width: 30, sortable: false, hidden: false },  
                    { name: 'faceImage', index: 'username', width: 50, sortable: false,
                        formatter:function(cellvalue, options, rowObject) {
                            <!-- 配置的虛擬目錄apiServer = http://192.168.199.150:8080 -->
                            var src = apiServer + cellvalue;
                            var img = "<img src='" + src + "' width='120'></img>"
                            return img;
                        }  
                    },
                    { name: 'username', index: 'password', width: 30, sortable: false },
                    { name: 'nickname', index: 'nickname', width: 30, sortable: false },
                    { name: 'fansCounts', index: 'age', width: 20, sortable: false },
                    { name: 'followCounts', index: 'sexValue', width: 20, sortable: false },
                    { name: 'receiveLikeCounts', index: 'province', width: 20, sortable: false, hidden: false }
                ],  
                viewrecords: true,          // 定義是否要顯示總記錄數
                rowNum: 10,                    // 在grid上顯示記錄條數,這個參數是要被傳遞到後臺
                rownumbers: true,              // 若是爲ture則會在表格左邊新增一列,顯示行順序號,從1開始遞增。此列名爲'rn'
                autowidth: true,              // 若是爲ture時,則當表格在首次被建立時會根據父元素比例從新調整表格寬度。若是父元素寬度改變,爲了使表格寬度可以自動調整則須要實現函數:setGridWidth
                height: 500,                // 表格高度,能夠是數字,像素值或者百分比
                rownumWidth: 36,             // 若是rownumbers爲true,則能夠設置行號 的寬度
                pager: "#usersListPager",        // 分頁控件的id  
                subGrid: false                // 是否啓用子表格
            }).navGrid('#usersListPager', {
                edit: false,
                add: false,
                del: false,
                search: false
            });
            
      
            // 隨着窗口的變化,設置jqgrid的寬度  
            $(window).bind('resize', function () {  
                var width = $('.usersList_wrapper').width()*0.99;  
                jqGrid.setGridWidth(width);  
            });  
            
            // 不顯示水平滾動條
            jqGrid.closest(".ui-jqgrid-bdiv").css({ "overflow-x" : "hidden" });
            
            // 條件查詢全部用戶列表
            $("#searchUserListButton").click(function(){
                var searchUsersListForm = $("#searchUserListForm");
                jqGrid.jqGrid().setGridParam({ 
                    page: 1,
                    url: hdnContextPath + "/users/list.action?" + searchUsersListForm.serialize(),
                }).trigger("reloadGrid");
            });
        }

後端獲取用戶列表分頁數據的接口:前端

@PostMapping("/list")
    @ResponseBody
    public PagedResult list(Users user , Integer page) {
        
        PagedResult result = usersService.queryUsers(user, page == null ? 1 : page, 10);
        return result;
    }

clipboard.png
搜索功能的實現:java

<!-- 搜索內容 -->
        <div class="col-md-12">
            <br/>
                <form id="searchUserListForm" class="form-inline" method="post" role="form">
                    <div class="form-group">
                        <label class="sr-only" for="username">用戶名:</label>
                        <input id="username" name="username" type="text" class="form-control" placeholder="用戶名" />
                    </div>
                    <div class="form-group">
                        <label class="sr-only" for="nickname">暱稱:</label>
                        <input id="nickname" name="nickname" type="text" class="form-control" placeholder="暱稱" />
                    </div>
                    <button id="searchUserListButton" class="btn yellow-casablanca" type="button">搜    索</button>
                </form>
            </div>

使用jGrid發送請求給後臺node

// 條件查詢全部用戶列表
        $("#searchUserListButton").click(function(){
            var searchUsersListForm = $("#searchUserListForm");
            jqGrid.jqGrid().setGridParam({ 
                page: 1,
                url: hdnContextPath + "/users/list.action?" + searchUsersListForm.serialize(),
            }).trigger("reloadGrid");
        });

2、背景音樂BGM的上傳、查詢和刪除

clipboard.png

上傳

$("#file").fileupload({
        pasteZone: "#bgmContent",
        dataType: "json",
        done: function(e, data) {
            console.log(data);
            
            if (data.result.status != '200') {
                alert("長傳失敗...");
            } else {
                var bgmServer = $("#bgmServer").val();
                var url = bgmServer + data.result.data;
                $("#bgmContent").html("<a href='" + url + "' target='_blank'>點我播放</a>");
                $("#path").attr("value", data.result.data);
            }
            
        }
    });

後臺接口保存BGM的方法參考上傳頭像的方法linux

clipboard.png

分頁查詢

參考用戶列表信息的分頁查詢多少git

刪除BGM

var deleteBgm = function(bgmId) {
    var flag = window.confirm("是否確認刪除???");
    if (!flag) {
        return;
    }
    $.ajax({
        url: $("#hdnContextPath").val() + '/video/delBgm.action?bgmId=' + bgmId,
        type: "POST",
        success: function(data) {
            if (data.status == 200 && data.msg == 'OK') {
                alert('刪除成功~~');
                var jqGrid = $("#bgmList");  
                jqGrid.jqGrid().trigger("reloadGrid");
            }
        }
    })
}

3、舉報管理

禁止播放

clipboard.png

var forbidVideo = function(videoId) {
    var flag = window.confirm("是否禁播");
    if (!flag) {
        return;
    }
    $.ajax({
        url: $("#hdnContextPath").val() + "/video/forbidVideo.action?videoId=" + videoId,
        type: "POST",
        async: false,
        success: function(data) {
            if(data.status == 200 && data.msg == "OK") {
                alert("操做成功");
                var jqGrid = $("#usersReportsList");  
                //reloadGrid是從新加載表格
                jqGrid.jqGrid().trigger("reloadGrid");
            } else {
                console.log(JSON.stringify(data));
            }
        }
    })
}

4、後臺管理系統增長或刪除BGM,向zookeeper-server建立子節點,讓小程序後端監聽【重點】

一、首先安裝Zookeeper到Linux上,啓動服務器

clipboard.png

二、編寫zk客戶端代碼:

import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZKCurator {
    //zk客戶端
    private CuratorFramework client = null;
    
    final static Logger log = LoggerFactory.getLogger(ZKCurator.class);
 
    public ZKCurator(CuratorFramework client) {
        this.client = client;
    }
    
    public void init() {
        client = client.usingNamespace("admin");
        try {
            //判斷在admin命名空間下是否有bgm節點 /admin/bgm
            if( client.checkExists().forPath("/bgm") == null ) {
                //對於zk來說,有兩種類型的節點,一種是持久節點(永久存在,除非手動刪除),另外一種是臨時節點(會話斷開,自動刪除)
                client.create().creatingParentContainersIfNeeded()
                .withMode(CreateMode.PERSISTENT) //持久節點
                .withACL(Ids.OPEN_ACL_UNSAFE)  //匿名權限
                .forPath("/bgm"); 
                log.info("zookeeper客戶端鏈接初始化成功");
                log.info("zookeeper服務端狀態:{}",client.isStarted());
            }
        } catch (Exception e) {
            log.error("zookeeper客戶端鏈接初始化失敗");
            e.printStackTrace();
        }
    }
    
    /**
     * 增長或者刪除Bgm,向zk-server建立子節點,供小程序後端監聽
     * @param bgmId
     * @param operType
     */
    public void sendBgmOperator(String bgmId, String operObject) {
        try {
            client.create().creatingParentContainersIfNeeded()
            .withMode(CreateMode.PERSISTENT) //持久節點
            .withACL(Ids.OPEN_ACL_UNSAFE)  //匿名權限
            .forPath("/bgm/" + bgmId, operObject.getBytes());

        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
}

三、在applicationContext-zookeeper.xml配置zookeeper:

<!-- 建立重連策列 -->
    <bean id="retryPolicy" class="org.apache.curator.retry.ExponentialBackoffRetry">
        <!-- 每次重試鏈接的等待時間 -->
        <constructor-arg index="0" value="1000"></constructor-arg>
        <!-- 設置最大的重連次數 -->
        <constructor-arg index="1" value="5"></constructor-arg>
    </bean>
    
    <!-- 建立zookeeper客戶端  -->
    <bean id="client" class="org.apache.curator.framework.CuratorFrameworkFactory"
        factory-method="newClient" init-method="start">
        <constructor-arg index="0" value="120.79.18.35:2181"></constructor-arg>
        <constructor-arg index="1" value="10000"></constructor-arg>
        <constructor-arg index="2" value="10000"></constructor-arg>
        <constructor-arg index="3" ref="retryPolicy"></constructor-arg>
    </bean>
     
     <!-- 調用init方法啓動 -->
    <bean id="ZKCurator" class="com.imooc.web.util.ZKCurator" init-method="init">
        <constructor-arg index="0" ref="client"></constructor-arg>
    </bean>

四、上傳或者刪除BGM時調用VideoServiceImpl.java的方法

@Autowired
    private ZKCurator zKCurator;
    @Override
    public void addBgm(Bgm bgm) {
        String id = sid.nextShort();
        bgm.setId(id);
        bgmMapper.insert(bgm);
        Map<String, String> map = new HashMap<>();
        map.put("operType", BGMOperatorTypeEnum.ADD.type);
        map.put("path", bgm.getPath());
        zKCurator.sendBgmOperator(id, JSONUtils.toJSONString(map));
    }

    @Override
    public void deleteBgm(String id) {
        Bgm bgm = bgmMapper.selectByPrimaryKey(id);
        
        bgmMapper.deleteByPrimaryKey(id);
        Map<String, String> map = new HashMap<>();
        map.put("operType", BGMOperatorTypeEnum.DELETE.type);
        map.put("path", bgm.getPath());
        zKCurator.sendBgmOperator(id, JSONUtils.toJSONString(map));
    }

五、小程序編寫代碼監聽zookeeper的節點,並對其作出相應的刪除和上傳操做【重點】

初始化zookeeper客戶端web

private CuratorFramework client = null;

    final static Logger log = LoggerFactory.getLogger(ZKCuratorClient.class);

//    public static final String ZOOKEEPER_SERVER = "120.79.18.36:2181";

    public void init() {
        if(client != null) {
            return;
        }
        //重試策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 5);
        //建立zk客戶端  120.79.18.36:2181
        client = CuratorFrameworkFactory.builder().connectString(resourceConfig.getZookeeperServer()).sessionTimeoutMs(10000)
                .retryPolicy(retryPolicy).namespace("admin").build();
        //啓動客戶端
        client.start();
        try {
            addChildWatch("/bgm");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

監聽zk-server的節點,當短視頻後臺管理系統上傳或者刪除某個BGM的時候,小程序後臺服務器經過Zookeeper監聽自動下載背景音樂ajax

public void addChildWatch(String nodePath) throws Exception {
        final PathChildrenCache cache = new PathChildrenCache(client, nodePath, true);
        cache.start();
        cache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                if(event.getType().equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
                    log.info("監聽到事件CHILD_ADDED");
                    //1. 從數據庫查詢bgm對象,獲取路徑Path
                    String path = event.getData().getPath();
                    String operatorObjStr = new String(event.getData().getData());
                    Map<String, String> map = JsonUtils.jsonToPojo(operatorObjStr, Map.class);
                    String operatorType = map.get("operType");
                    String songPath = map.get("path");

//                    String[] arr = path.split("/");
//                    String bgmId = arr[arr.length-1];
//                    Bgm bgm =  bgmService.queryBgmById(bgmId);
//                    if(bgm == null){
//                        return;
//                    }
                    //1.1 bgm所在的相對路徑
//                String songPath = bgm.getPath();

                    //2. 定義保存到本地的bgm路徑
//                String filePath = "E:\\imooc_videos_dev" + songPath;
                    String filePath = resourceConfig.getFileSpace() + songPath;

                    //3. 定義下載的路徑(播放url)
                    String[] arrPath = songPath.split("\\\\"); //windows
//                    String[] arrPath = songPath.split("/"); //linux
                    String finalPath = "";
                    //3.1 處理url的斜槓以及編碼
                    for(int i=0; i<arrPath.length;i++){
                        if(StringUtils.isNotBlank(arrPath[i])) {
                            finalPath += "/";
                            finalPath += URLEncoder.encode(arrPath[i], "UTF-8");
                        }
                    }
//                    String bgmUrl = "http://192.168.199.150:8080/mvc" + finalPath;
                    String bgmUrl = resourceConfig.getBgmServer() + finalPath;

                    if(operatorType.equals("1")){
                        //下載bgm到springboot服務器
                        URL url = new URL(bgmUrl);
                        File file = new File(filePath);
                        FileUtils.copyURLToFile(url, file);
                        client.delete().forPath(path);
                    }else if(operatorType.equals("2")){
                        File file = new File(filePath);
                        FileUtils.forceDelete(file);
                        client.delete().forPath(path);
                    }

                }
            }
        });
    }
相關文章
相關標籤/搜索