• Java项目硅谷课堂学习笔记-P8点播模块管理-后台-管理员端


    1.课程统计

    1.1功能介绍

    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述

    1.2实体类

    package com.jq.model.vod;
    
    import com.jq.model.base.BaseEntity;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableName;
    import com.fasterxml.jackson.annotation.JsonFormat;
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    import java.util.Date;
    
    @Data
    @ApiModel(description = "VideoVisitor")
    @TableName("video_visitor")
    public class VideoVisitor extends BaseEntity {
    
    	private static final long serialVersionUID = 1L;
    
    	@ApiModelProperty(value = "课程id")
    	@TableField("course_id")
    	private Long courseId;
    
    	@ApiModelProperty(value = "视频id")
    	@TableField("video_id")
    	private Long videoId;
    
    	@ApiModelProperty(value = "来访者用户id")
    	@TableField("user_id")
    	private Long userId;
    
    	@ApiModelProperty(value = "昵称")
    	@TableField("nick_name")
    	private String nickName;
    
    	@ApiModelProperty(value = "进入时间")
    	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    	@TableField("join_time")
    	private Date joinTime;
    
    	@ApiModelProperty(value = "离开的时间")
    	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    	@TableField("leave_time")
    	private Date leaveTime;
    
    	@ApiModelProperty(value = "用户停留的时间(单位:秒)")
    	@TableField("duration")
    	private Long duration;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    package com.jq.vo.vod;
    
    import com.fasterxml.jackson.annotation.JsonFormat;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    import java.util.Date;
    
    @Data
    public class VideoVisitorCountVo {
    
    	@ApiModelProperty(value = "进入时间")
    	@JsonFormat(pattern = "yyyy-MM-dd")
    	private Date joinTime;
    
    	@ApiModelProperty(value = "用户个数")
    	private Integer userCount;
    
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.3controller层

    package com.jq.vod.controller;
    
    
    import com.jq.result.Result;
    import com.jq.vod.service.VideoVisitorService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.Map;
    
    /**
     * 

    * 视频来访者记录表 前端控制器 *

    * * @author CJQ * @since 2022-08-15 */
    @RestController @RequestMapping("/admin/vod/videoVisitor") @CrossOrigin //解决跨域问题 public class VideoVisitorController { @Autowired private VideoVisitorService videoVisitorService; //课程统计接口 @GetMapping("findCount/{courseId}/{startDate}/{endDate}") public Result findCount(@PathVariable Long courseId, @PathVariable String startDate, @PathVariable String endDate ){ Map<String,Object>map =videoVisitorService.findCount(courseId,startDate,endDate); return Result.ok(map); } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    1.4serviceImpl层

    package com.jq.vod.service.impl;
    
    
    import com.jq.model.vod.VideoVisitor;
    import com.jq.vo.vod.VideoVisitorCountVo;
    import com.jq.vod.mapper.VideoVisitorMapper;
    import com.jq.vod.service.VideoVisitorService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    
    import java.util.*;
    import java.util.stream.Collectors;
    
    /**
     * 

    * 视频来访者记录表 服务实现类 *

    * * @author CJQ * @since 2022-08-15 */
    @Service public class VideoVisitorServiceImpl extends ServiceImpl<VideoVisitorMapper, VideoVisitor> implements VideoVisitorService { /** * 课程统计接口 * @param courseId * @param startDate * @param endDate * @return */ @Override public Map<String, Object> findCount(Long courseId, String startDate, String endDate) { //调用mapper方法 List<VideoVisitorCountVo>videoVisitorVoList =baseMapper.findCount(courseId,startDate,endDate); //创建一个map集合 Map<String,Object>map=new HashMap<>(); //创建两个list集合,一个代表所有日期,一个代表日期对应的数量 //封装数据 所有日期 List<String> dateList = videoVisitorVoList.stream() .map(VideoVisitorCountVo::getJoinTime) .collect(Collectors.toList()); //代表日期对应的数量 List<Integer>countList=videoVisitorVoList.stream() .map(VideoVisitorCountVo::getUserCount) .collect(Collectors.toList()); //放入map map.put("xData", dateList); map.put("yData", countList); return map; } }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    1.5mapper层

    package com.jq.vod.mapper;
    
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jq.model.vod.VideoVisitor;
    import com.jq.vo.vod.VideoVisitorCountVo;
    import org.apache.ibatis.annotations.Param;
    
    import java.util.List;
    
    /**
     * 

    * 视频来访者记录表 Mapper 接口 *

    * * @author CJQ * @since 2022-08-15 */
    public interface VideoVisitorMapper extends BaseMapper<VideoVisitor> { List<VideoVisitorCountVo> findCount(@Param("courseId") Long courseId, @Param("startDate") String startDate, @Param("endDate") String endDate); }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.jq.vod.mapper.VideoVisitorMapper">
    
        <select id="findCount" resultType="com.jq.vo.vod.VideoVisitorCountVo">
            SELECT
            DATE(join_time) AS joinTime,
            COUNT(*) AS userCount
            FROM video_visitor
            <where>
                <if test="startDate != null and startDate != ''">
                    AND DATE(join_time) >= #{startDate}
                if>
                <if test="endDate != null and endDate != ''">
                    AND DATE(join_time) <= #{endDate}
                if>
                and course_id=#{courseId}
            where>
            GROUP BY DATE(join_time)
            ORDER BY DATE(join_time)
        select>
    mapper>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    1.6安装ECharts组件

    ECharts是百度的一个项目,后来百度把Echart捐给apache,用于图表展示,提供了常规的折线图柱状图散点图饼图K线图,用于统计的盒形图,用于地理数据可视化的地图热力图线图,用于关系数据可视化的关系图treemap旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图仪表盘,并且支持图与图之间的混搭。

    官方网站

    1.7前端接口videoVisitor.js

    import request from '@/utils/request'
    
    const api_name = '/admin/vod/videoVisitor'
    
    export default {
    
      findCount(courseId, startDate, endDate) {
        return request({
          url: `${api_name}/findCount/${courseId}/${startDate}/${endDate}`,
          method: 'get'
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    1.8前端页面

    <template>
      <div class="app-container">
        <!--表单-->
        <el-form :inline="true" class="demo-form-inline">
          <el-form-item>
            <el-date-picker
              v-model="startDate"
              type="date"
              placeholder="选择开始日期"
              value-format="yyyy-MM-dd" />
          </el-form-item>
          <el-form-item>
            <el-date-picker
              v-model="endDate"
              type="date"
              placeholder="选择截止日期"
              value-format="yyyy-MM-dd" />
          </el-form-item>
          <el-button
            :disabled="btnDisabled"
            type="primary"
            icon="el-icon-search"
            @click="showChart()">查询</el-button>
        </el-form>
        <div id="chart" class="chart" style="height:500px;" />
      </div>
    </template>
    <script>
    import echarts from 'echarts'
    import api from '@/api/vod/videoVisitor'
    
    export default {
      data() {
        return {
          courseId: '',
          startDate: '',
          endDate: '',
          btnDisabled: false
        }
      },
      created() {
        this.courseId = this.$route.params.id
        // 初始化最近十天数据
        let currentDate = new Date();
        this.startDate = this.dateFormat(new Date(currentDate.getTime()-7*24*3600*1000))
        this.endDate = this.dateFormat(currentDate)
        this.showChart()
      },
      methods: {
        showChart() {
          api.findCount(this.courseId, this.startDate, this.endDate).then(response => {
            this.setChartData(response.data)
          })
        },
        setChartData(data) {
          // 基于准备好的dom,初始化echarts实例
          var myChart = echarts.init(document.getElementById('chart'))
          // 指定图表的配置项和数据
          var option = {
            title: {
              text: '观看课程人数统计'
            },
            xAxis: {
              data: data.xData
            },
            yAxis: {
              minInterval: 1
            },
            series: [{
              type: 'line',
              data: data.yData
            }]
          }
          // 使用刚指定的配置项和数据显示图表。
          myChart.setOption(option)
        },
        dateFormat(date) {
          let fmt = 'YYYY-mm-dd'
          let ret;
          const opt = {
            "Y+": date.getFullYear().toString(),        // 年
            "m+": (date.getMonth() + 1).toString(),     // 月
            "d+": date.getDate().toString(),            // 日
            "H+": date.getHours().toString(),           // 时
            "M+": date.getMinutes().toString(),         // 分
            "S+": date.getSeconds().toString()          // 秒
            // 有其他格式化字符需求可以继续添加,必须转化成字符串
          };
          for (let k in opt) {
            ret = new RegExp("(" + k + ")").exec(fmt);
            if (ret) {
              fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
            };
          };
          return fmt;
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    2.整合腾讯云点播

    请添加图片描述

    2.0腾讯云点播

    腾讯云点播(Video on Demand,VOD)基于腾讯多年技术积累与基础设施建设,为有音视频应用相关需求的客户提供包括音视频存储管理、音视频转码处理、音视频加速播放和音视频通信服务的一站式解决方案。
    在这里插入图片描述文档中心

    在这里插入图片描述

    控制台
    在这里插入图片描述在这里插入图片描述视频播放依赖下面
    在这里插入图片描述
    视频上传
    在这里插入图片描述

    2.1上传视频到腾讯云

    2.1.1引入依赖

    <dependency>
        <groupId>com.qcloudgroupId>
        <artifactId>vod_apiartifactId>
        <version>2.1.4version>
        <exclusions>
            <exclusion>
                <groupId>org.slf4jgroupId>
                <artifactId>slf4j-log4j12artifactId>
            exclusion>
        exclusions>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.1.2controller层

        /**
         * 上传视频
         * @return
         */
        @PostMapping("upload")
        public Result upload (){
            String fieldId=vodService.updateVideo();
            return Result.ok(fieldId);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.1.3serviceImpl层

    package com.jq.vod.service.impl;
    
    import com.jq.exception.GgktException;
    import com.jq.vod.service.VodService;
    import com.jq.vod.utils.ConstantPropertiesUtil;
    import com.qcloud.vod.VodUploadClient;
    import com.qcloud.vod.model.VodUploadRequest;
    import com.qcloud.vod.model.VodUploadResponse;
    import org.springframework.stereotype.Service;
    
    @Service
    public class VodServiceImpl implements VodService {
        //上传视频
        @Override
        public String updateVideo() {
            //指定腾讯云账号和id
            VodUploadClient client = new VodUploadClient(ConstantPropertiesUtil.ACCESS_KEY_ID,
                    ConstantPropertiesUtil.ACCESS_KEY_SECRET);
            //上传请求对象
            VodUploadRequest request = new VodUploadRequest();
            //设置上传视频所在路径
            request.setMediaFilePath("/data/videos/Wildlife.wmv");
            //任务流
            request.setProcedure("LongVideoPreset");
            try {
                //调用方法上传视频
                VodUploadResponse response = client.upload("ap-guangzhou", request);
                //获取上传之后的视频id
                String fileId = response.getFileId();
                return fileId;
            } catch (Exception e) {
                // 业务方进行异常处理
                throw new GgktException(20001,"上传视频失败");
            }
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    2.1.4前端接口

    import request from '@/utils/request'
    
    export default {
      //删除视频
      removeByVodId(id) {
        return request({
          url: `/admin/vod/remove/${id}`,
          method: 'delete'
        })
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.1.5前端页面

    <template>
      <!-- 添加和修改课时表单 -->
      <el-dialog :visible="dialogVisible" title="添加课时" @close="close()">
        <el-form :model="video" label-width="120px">
          <el-form-item label="课时标题">
            <el-input v-model="video.title"/>
          </el-form-item>
          <el-form-item label="课时排序">
            <el-input-number v-model="video.sort" :min="0" />
          </el-form-item>
          <el-form-item label="是否免费">
            <el-radio-group v-model="video.isFree">
              <el-radio :label="0">免费</el-radio>
              <el-radio :label="1">默认</el-radio>
            </el-radio-group>
          </el-form-item>
          <!-- 上传视频 -->
          <el-form-item label="上传视频">
            <el-upload
              ref="upload"
              :auto-upload="false"
              :on-success="handleUploadSuccess"
              :on-error="handleUploadError"
              :on-exceed="handleUploadExceed"
              :file-list="fileList"
              :limit="1"
              :before-remove="handleBeforeRemove"
              :on-remove="handleOnRemove"
              :action="BASE_API+'/admin/vod/upload'">
              <el-button slot="trigger" size="small" type="primary">选择视频</el-button>
              <el-button
                :disabled="uploadBtnDisabled"
                style="margin-left: 10px;"
                size="small"
                type="success"
                @click="submitUpload()">上传</el-button>
            </el-upload>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="close()">取 消</el-button>
          <el-button type="primary" @click="saveOrUpdate()">确 定</el-button>
        </div>
      </el-dialog>
    </template>
    <script>
    import videoApi from '@/api/vod/video'
    import vodApi from '@/api/vod/vod'
    export default {
      data() {
        return {
          BASE_API: 'http://localhost:8301',
          dialogVisible: false,
          video: {
            sort: 0,
            free: false
          },
          fileList: [], // 上传文件列表
          uploadBtnDisabled: false
        }
      },
      methods: {
        open(chapterId, videoId) {
          this.dialogVisible = true
          this.video.chapterId = chapterId
          if (videoId) {
            videoApi.getById(videoId).then(response => {
              this.video = response.data
              // 回显
              if (this.video.videoOriginalName) {
                this.fileList = [{ 'name': this.video.videoOriginalName }]
              }
            })
          }
        },
        close() {
          this.dialogVisible = false
          // 重置表单
          this.resetForm()
        },
        resetForm() {
          this.video = {
            sort: 0,
            free: false
          }
          this.fileList = [] // 重置视频上传列表
        },
        saveOrUpdate() {
          if (!this.video.id) {
            this.save()
          } else {
            this.update()
          }
        },
        save() {
          this.video.courseId = this.$parent.$parent.courseId
          videoApi.save(this.video).then(response => {
            this.$message.success(response.message)
            // 关闭组件
            this.close()
            // 刷新列表
            this.$parent.fetchNodeList()
          })
        },
        update() {
          videoApi.updateById(this.video).then(response => {
            this.$message.success(response.message)
            // 关闭组件
            this.close()
            // 刷新列表
            this.$parent.fetchNodeList()
          })
        },
        // 上传多于一个视频
        handleUploadExceed(files, fileList) {
          this.$message.warning('想要重新上传视频,请先删除已上传的视频')
        },
        // 上传
        submitUpload() {
          this.uploadBtnDisabled = true
          this.$refs.upload.submit() // 提交上传请求
        },
        // 视频上传成功的回调
        handleUploadSuccess(response, file, fileList) {
          this.uploadBtnDisabled = false
          this.video.videoSourceId = response.data
          this.video.videoOriginalName = file.name
        },
        // 失败回调
        handleUploadError() {
          this.uploadBtnDisabled = false
          this.$message.error('上传失败2')
        },
        // 删除视频文件确认
        handleBeforeRemove(file, fileList) {
          return this.$confirm(`确定移除 ${file.name}`)
        },
        // 执行视频文件的删除
        handleOnRemove(file, fileList) {
          if (!this.video.videoSourceId) {
            return
          }
          vodApi.removeByVodId(this.video.videoSourceId).then(response => {
            this.video.videoSourceId = ''
            this.video.videoOriginalName = ''
            videoApi.updateById(this.video)
            this.$message.success(response.message)
          })
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152

    2.2上传视频到腾讯云其他方式

    在这里插入图片描述

    2.2.1申请上传签名

    在这里插入图片描述

    2.2.2申请上传签名所用类

    package com.jq.vod.utils;
    
    import sun.misc.BASE64Encoder;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.util.Random;
    
    public class Signature {
        private String secretId;
        private String secretKey;
        private long currentTime;
        private int random;
        private int signValidDuration;
        private static final String HMAC_ALGORITHM = "HmacSHA1"; //签名算法
        private static final String CONTENT_CHARSET = "UTF-8";
        public static byte[] byteMerger(byte[] byte1, byte[] byte2) {
            byte[] byte3 = new byte[byte1.length + byte2.length];
            System.arraycopy(byte1, 0, byte3, 0, byte1.length);
            System.arraycopy(byte2, 0, byte3, byte1.length, byte2.length);
            return byte3;
        }
        // 获取签名
        public String getUploadSignature() throws Exception {
            String strSign = "";
            String contextStr = "";
            // 生成原始参数字符串
            long endTime = (currentTime + signValidDuration);
            contextStr += "secretId=" + java.net.URLEncoder.encode(secretId, "utf8");
            contextStr += "¤tTimeStamp=" + currentTime;
            contextStr += "&expireTime=" + endTime;
            contextStr += "&random=" + random;
            //设置任务流
            contextStr += "&procedure=LongVideoPreset";
            try {
                Mac mac = Mac.getInstance(HMAC_ALGORITHM);
                SecretKeySpec secretKey = new SecretKeySpec(this.secretKey.getBytes(CONTENT_CHARSET), mac.getAlgorithm());
                mac.init(secretKey);
                byte[] hash = mac.doFinal(contextStr.getBytes(CONTENT_CHARSET));
                byte[] sigBuf = byteMerger(hash, contextStr.getBytes("utf8"));
                strSign = base64Encode(sigBuf);
                strSign = strSign.replace(" ", "").replace("\n", "").replace("\r", "");
            } catch (Exception e) {
                throw e;
            }
            return strSign;
        }
        private String base64Encode(byte[] buffer) {
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(buffer);
        }
        public void setSecretId(String secretId) {
            this.secretId = secretId;
        }
        public void setSecretKey(String secretKey) {
            this.secretKey = secretKey;
        }
        public void setCurrentTime(long currentTime) {
            this.currentTime = currentTime;
        }
        public void setRandom(int random) {
            this.random = random;
        }
        public void setSignValidDuration(int signValidDuration) {
            this.signValidDuration = signValidDuration;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    2.2.3申请上传签名controller层

        //返回客户端上传视频签名
        @GetMapping("sign")
        public Result sign(){
            Signature sign = new Signature();
            // 设置 App 的云 API 密钥
            sign.setSecretId(ConstantPropertiesUtil.ACCESS_KEY_ID);
            sign.setSecretKey(ConstantPropertiesUtil.ACCESS_KEY_SECRET);
            sign.setCurrentTime(System.currentTimeMillis() / 1000);
            sign.setRandom(new Random().nextInt(java.lang.Integer.MAX_VALUE));
            sign.setSignValidDuration(3600 * 24 * 2); // 签名有效期:2天
            try {
                String signature = sign.getUploadSignature();
                System.out.println("signature : " + signature);
                return  Result.ok(signature);
            } catch (Exception e) {
                System.out.print("获取签名失败");
                e.printStackTrace();
                throw new GgktException(20001,"获取签名失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2.4web端上传sdk

    在这里插入图片描述

    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>QCloud VIDEO UGC UPLOAD SDK</title>
      <link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
      <style type="text/css">
        .text-danger {
          color: red;
        }
    
        .control-label {
          text-align: left !important;
        }
    
        #resultBox {
          width: 100%;
          height: 300px;
          border: 1px solid #888;
          padding: 5px;
          overflow: auto;
          margin-bottom: 20px;
        }
    
        .uploaderMsgBox {
          width: 100%;
          border-bottom: 1px solid #888;
        }
    
        .cancel-upload {
          text-decoration: none;
          cursor: pointer;
        }
      </style>
    </head>
    <body>
      <div id="content">
        <div class="container">
          <h1>UGC-Uploader</h1>
        </div>
      </div>
      <div class="container" id="main-area">
        <div class="row" style="padding:10px;">
          <p>
            示例1点击“直接上传视频”按钮即可上传视频。<br></p>
        </div>
        <form ref="vExample">
          <input type="file" style="display:none;" ref="vExampleFile" @change="vExampleUpload" />
        </form>
        <div class="row" style="padding:10px;">
          <h4>示例1:直接上传视频</h4>
          <a href="javascript:void(0);" class="btn btn-default" @click="vExampleAdd">直接上传视频</a>
        </div>
          <!-- 上传信息组件	 -->
          <div class="uploaderMsgBox" v-for="uploaderInfo in uploaderInfos">
            <div v-if="uploaderInfo.videoInfo">
              视频名称:{{uploaderInfo.videoInfo.name + '.' + uploaderInfo.videoInfo.type}};
              上传进度:{{Math.floor(uploaderInfo.progress * 100) + '%'}};
              fileId:{{uploaderInfo.fileId}};
              上传结果:{{uploaderInfo.isVideoUploadCancel ? '已取消' : uploaderInfo.isVideoUploadSuccess ? '上传成功' : '上传中'}}<br>
              地址:{{uploaderInfo.videoUrl}}<a href="javascript:void(0);" class="cancel-upload" v-if="!uploaderInfo.isVideoUploadSuccess && !uploaderInfo.isVideoUploadCancel" @click="uploaderInfo.cancel()">取消上传</a><br>
            </div>
            <div v-if="uploaderInfo.coverInfo">
              封面名称:{{uploaderInfo.coverInfo.name}};
              上传进度:{{Math.floor(uploaderInfo.coverProgress * 100) + '%'}};
              上传结果:{{uploaderInfo.isCoverUploadSuccess ? '上传成功' : '上传中'}}<br>
              地址:{{uploaderInfo.coverUrl}}<br>
            </div>
          </div>
      </div>
      <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
      <script src="//cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
      <script src="https://cdn-go.cn/cdn/vod-js-sdk-v6/latest/vod-js-sdk-v6.js"></script>
    
      <script type="text/javascript">
    
        ;(function () {
    
          /**
           * 计算签名。调用签名接口获取
          **/
          function getSignature() {
            return axios.get("http://localhost:8301/admin/vod/user/sign").then(response =>{
              return response.data.data
            })
          };
          var app = new Vue({
            el: '#main-area',
            data: {
              uploaderInfos: [],
    
              vcExampleVideoName: '',
              vcExampleCoverName: '',
    
              cExampleFileId: '',
            },
            created: function () {
              this.tcVod = new TcVod.default({
                getSignature: getSignature
              })
            },
            methods: {
              /**
               * vExample示例。添加视频
              **/
              vExampleAdd: function () {
                this.$refs.vExampleFile.click()
              },
              /**
               * vExample示例。上传视频过程。
              **/
              vExampleUpload: function () {
                var self = this;
                var mediaFile = this.$refs.vExampleFile.files[0]
    
                var uploader = this.tcVod.upload({
                  mediaFile: mediaFile,
                })
                uploader.on('media_progress', function (info) {
                  uploaderInfo.progress = info.percent;
                })
                uploader.on('media_upload', function (info) {
                  uploaderInfo.isVideoUploadSuccess = true;
                })
    
                console.log(uploader, 'uploader')
    
                var uploaderInfo = {
                  videoInfo: uploader.videoInfo,
                  isVideoUploadSuccess: false,
                  isVideoUploadCancel: false,
                  progress: 0,
                  fileId: '',
                  videoUrl: '',
                  cancel: function() {
                    uploaderInfo.isVideoUploadCancel = true;
                    uploader.cancel()
                  },
                }
    
                this.uploaderInfos.push(uploaderInfo)
                uploader.done().then(function(doneResult) {
                  console.log('doneResult', doneResult)
                  uploaderInfo.fileId = doneResult.fileId;
                  return doneResult.video.url;
                }).then(function (videoUrl) {
                  uploaderInfo.videoUrl = videoUrl
                  self.$refs.vExample.reset();
                })
              },
              // cExample 上传过程
              cExampleUpload: function() {
                var self = this;
                var coverFile = this.$refs.cExampleCover.files[0];
    
                var uploader = this.tcVod.upload({
                  fileId: this.cExampleFileId,
                  coverFile: coverFile,
                })
                uploader.on('cover_progress', function(info) {
                  uploaderInfo.coverProgress = info.percent;
                })
                uploader.on('cover_upload', function(info) {
                  uploaderInfo.isCoverUploadSuccess = true;
                })
                console.log(uploader, 'uploader')
    
                var uploaderInfo = {
                  coverInfo: uploader.coverInfo,
                  isCoverUploadSuccess: false,
                  coverProgress: 0,
                  coverUrl: '',
                  cancel: function () {
                    uploader.cancel()
                  },
                }
    
                this.uploaderInfos.push(uploaderInfo)
    
                uploader.done().then(function (doneResult) {
                  console.log('doneResult', doneResult)
                  uploaderInfo.coverUrl = doneResult.cover.url;
                  self.$refs.cExample.reset();
                })
              },
            },
          })
        })();
    
      </script>
      <!-- Global site tag (gtag.js) - Google Analytics -->
      <script async src="https://www.googletagmanager.com/gtag/js?id=UA-26476625-7"></script>
      <script>
        // add by alsotang@gmail.com
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-26476625-7');
      </script>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210

    2.3删除腾讯云中的视频

    2.3.1controller层

    /**
         * 删除腾讯云视频
         */
        @DeleteMapping("remove/{fieldId}")
        public Result remove(@PathVariable String fieldId){
            vodService.removeVideo(fieldId);
            return  Result.ok(null);
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.3.2serviceImpl层

    /**
         * 删除视频
         * @param fieldId
         */
        @Override
        public void removeVideo(String fieldId) {
            try{
                // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,
                Credential cred = new Credential(ConstantPropertiesUtil.ACCESS_KEY_ID,
                        ConstantPropertiesUtil.ACCESS_KEY_SECRET);
                // 实例化一个http选项,可选的,没有特殊需求可以跳过
                HttpProfile httpProfile = new HttpProfile();
                httpProfile.setEndpoint("vod.tencentcloudapi.com");
                // 实例化一个client选项,可选的,没有特殊需求可以跳过
                ClientProfile clientProfile = new ClientProfile();
                clientProfile.setHttpProfile(httpProfile);
                // 实例化要请求产品的client对象,clientProfile是可选的
                VodClient client = new VodClient(cred, "", clientProfile);
                // 实例化一个请求对象,每个接口都会对应一个request对象
                DeleteMediaRequest req = new DeleteMediaRequest();
                req.setFileId(fieldId);
                // 返回的resp是一个DeleteMediaResponse的实例,与请求对象对应
                DeleteMediaResponse resp = client.DeleteMedia(req);
                // 输出json格式的字符串回包
                System.out.println(DeleteMediaResponse.toJsonString(resp));
            } catch (TencentCloudSDKException e) {
                System.out.println(e.toString());
                throw new GgktException(20001,"删除视频失败");
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2.3删除课程时,删除视频

        /**
         * 根据课程id删除小节,删除小节里面的视频
         * @param id
         */
        @Override
        public void removeVideoByCourseId(Long id) {
            //根据课程id查询课程中所有的小节
            QueryWrapper<Video>wrapper=new QueryWrapper<>();
            wrapper.eq("course_id",id);
            List<Video> videoList = baseMapper.selectList(wrapper);
            //遍历所有小节集合得到每个小节,获取每个小节视频id
            for(Video video:videoList){
                //获取每个小节视频id
                String videoSourceId = video.getVideoSourceId();
                //判断视频id是否为空,不为空,删除腾讯云视频
                if(StringUtils.isEmpty(videoSourceId)){
                    vodService.removeVideo(videoSourceId);
                }
            }
            
            
            //根据课程id删除课程所有小节
            baseMapper.delete(wrapper);
            
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.4删除小节时,删除视频

    /**
         * 删除小节时,删除视频
         * @param id
         */
        @Override
        public void removeVideoById(Long id) {
            //id 查询小节id
            Video video = baseMapper.selectById(id);
            // 获取video里面的视频id
            String videoSourceId = video.getVideoSourceId();
            //判断视频id是否为空
            if (StringUtils.isEmpty(videoSourceId)){
                //视频id不为空,调用方法根据视频id删除腾讯云视频
                vodService.removeVideo(videoSourceId);
            }
            //根据id删除小节
            baseMapper.deleteById(id);
            
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    18-spring 事务
    文件的上传与下载
    JUC笔记(二) --- Java线程
    第十八章:Swing自述
    软件工程评级B-,有大量调剂名额。北京联合大学考情分析
    Vue3:对ref、reactive的一个性能优化API
    Android 12 桌面去掉搜索框和 隐藏设置搜索框 隐藏应用页的搜索框
    Smobiler的复杂控件的由来与创造
    学网络安全可以参考什么方向?该怎么学?
    Azure Devops Create Project TF400711问题分析解决
  • 原文地址:https://blog.csdn.net/qq_45498432/article/details/126347010