![](https://i-blog.csdnimg.cn/blog_migrate/5e6dc3ea924df9238daf5edafb0eb84b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/daf2546d6a79f967ea783b5c1bc07d78.png)
下载依赖 npm i vod-js-sdk-v6
前端
/vod/course/Video/Form.vue
<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
class="upload-demo"
ref="upload"
action="#"
:http-request="uploadVideo"
:limit="1"
:on-remove="handleRemove"
:on-change="handleChange"
:auto-upload="false">
<el-button slot="trigger" size="small" type="primary">选取视频</el-button>
<el-button style="margin-left: 40px;" size="small" type="success" @click="submitUpload">点击上传</el-button>
<el-progress class="progress" :text-inside="true" :stroke-width="18" :percentage="progress" status="exception"></el-progress>
<div slot="tip" class="el-upload__tip">只能上传mp4文件,且不超过500M</div>
</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 TcVod from 'vod-js-sdk-v6'
import request from '@/utils/request'
export default {
data() {
return {
BASE_API: 'http://localhost:8301',
dialogVisible: false,
video: {
sort: 0,
free: false
},
fileList: [], // 上传文件列表
uploadBtnDisabled: false,
// 进度条百分比
progress: 0,
//图片原名称
filename:"",
fileId:"",
// 上传成功后的地址
videoURL: '',
}
},
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('想要重新上传视频,请先删除已上传的视频')
},
// 获取签名 这里的签名请求是由后端提供的,只需要拿到后端给的签名请求即可
getVodSignature () {
const url = this.BASE_API+'/admin/vod/sign'
return request.get(url).then(function (response) {
return response.data
})
},
// 文件列表改变时 将文件列表保存到本地
handleChange (file, fileList) {
this.fileList = fileList
this.filename = this.fileList[0].raw.name
},
// 上传
submitUpload() {
if (this.fileList.length < 1) return this.$message.warning('请先选取视频,再进行上传')
this.uploadVideo()
},
// 自定义上传
uploadVideo (e) {
// 当
console.log(this.fileList[0].raw)
if (this.fileList.length < 1) {
window.alert('您还没有选取文件')
} else {
//必须以函数的形式返回 sdk参数限制
const getSignature = async () => {
const data = await this.getVodSignature()
return data
}
const tcVod = new TcVod({
getSignature: getSignature // 获取上传签名的函数
})
// 获取通过elementui上传到本地的文件 因为参数类型必须为file 不能直接以对象的形式传输
const mediaFile = this.fileList[0].raw
const uploader = tcVod.upload({
mediaFile: mediaFile
})
// 监听上传进度
uploader.on('media_progress', info => {
this.progress = parseInt(info.percent * 100)
})
// 上传结束时,将url存到本地
uploader.done().then(doneResult => {
// 保存地址
// console.log(doneResult)
// console.log(this.fileId)
this.video.videoSourceId = doneResult.fileId
this.video.videoOriginalName = this.filename;
// 将视频的第一帧保存为封面 不需要封面的可以直接忽略掉以下代码
const canvas = document.createElement('canvas')
const img = document.getElementById('video_img')
const video = document.getElementById('video')
video.setAttribute('crossOrigin', 'anonymous')
canvas.width = video.clientWidth
canvas.height = video.clientHeight
video.onloadeddata = (res) => {
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height)
const dataURL = canvas.toDataURL('image/png')
img.setAttribute('src', dataURL)
// 拿到base64的字符串,并保存到本地
this.imgBase = dataURL.split(',')[1]
}
})
}
},
// 点击删除时
handleRemove (file, fileList) {
console.log(file, fileList.length)
}
},
}
</script>
/api/video.js
import request from '@/utils/request'
const api_name = '/admin/vod/video'
export default {
save(video) {
return request({
url: `${api_name}/save`,
method: 'post',
data: video
})
},
getById(id) {
return request({
url: `${api_name}/get/${id}`,
method: 'get'
})
},
updateById(video) {
return request({
url: `${api_name}/update`,
method: 'put',
data: video
})
},
removeById(id) {
return request({
url: `${api_name}/remove/${id}`,
method: 'delete'
})
}
}
后端
service-vod模块
TencentCloudVodController === (VodController)
package com.atguigu.ggkt.vod.controller;
import com.atguigu.ggkt.exception.GgktException;
import com.atguigu.ggkt.result.Result;
import com.atguigu.ggkt.vod.service.TencentCloudVodService;
import com.atguigu.ggkt.vod.utils.ConstantPropertiesUtil;
import com.atguigu.ggkt.vod.utils.Signature;
import com.baomidou.mybatisplus.extension.api.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.Random;
/**
* @Created with IntelliJ IDEA.
* @author:YanHui
* @date: 2023/01/05/19:31
* @msg:
*/
@Api(tags = "腾讯云点播")
@RestController
@RequestMapping("/admin/vod")
@CrossOrigin
public class TencentCloudVodController {
@ApiOperation("获取签名")
@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, "获取签名失败");
}
}
}
VideoController
package com.atguigu.ggkt.vod.controller;
import com.atguigu.ggkt.model.vod.Video;
import com.atguigu.ggkt.result.Result;
import com.atguigu.ggkt.vod.service.VideoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* <p>
* 课程视频 前端控制器
* </p>
*
* @author YanHui
* @since 2023-01-03
*/
@Api(tags = "课程小结(课时)")
@RestController
@RequestMapping(value="/admin/vod/video")
@CrossOrigin
public class VideoController {
@Resource
private VideoService videoService;
@ApiOperation(value = "获取")
@GetMapping("get/{id}")
public Result get(@PathVariable Long id) {
Video video = videoService.getById(id);
return Result.ok(video);
}
@ApiOperation(value = "新增")
@PostMapping("save")
public Result save(@RequestBody Video video) {
videoService.save(video);
return Result.ok(null);
}
@ApiOperation(value = "修改")
@PutMapping("update")
public Result updateById(@RequestBody Video video) {
videoService.updateVideoById(video);
return Result.ok(null);
}
@ApiOperation(value = "删除")
@DeleteMapping("remove/{id}")
public Result remove(@PathVariable Long id) {
videoService.removeVideoById(id);
return Result.ok(null);
}
}
VideoService
package com.atguigu.ggkt.vod.service;
import com.atguigu.ggkt.model.vod.Video;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 课程视频 服务类
* </p>
*
* @author YanHui
* @since 2023-01-03
*/
public interface VideoService extends IService<Video> {
/**
* 根据课程id删除小节
* @param id
*/
void removeByCourseId(Long id);
/**
* 根据id删除小节
* @param id
*/
void removeVideoById(Long id);
/**
* 根据id修改小节
* @param video
*/
void updateVideoById(Video video);
}
VideoServiceImpl
package com.atguigu.ggkt.vod.service.impl;
import com.atguigu.ggkt.model.vod.Video;
import com.atguigu.ggkt.vod.mapper.VideoMapper;
import com.atguigu.ggkt.vod.service.TencentCloudVodService;
import com.atguigu.ggkt.vod.service.VideoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 课程视频 服务实现类
* </p>
*
* @author YanHui
* @since 2023-01-03
*/
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, Video> implements VideoService {
@Resource
private TencentCloudVodService tencentCloudVodService;
@Override
public void removeByCourseId(Long id) {
LambdaQueryWrapper<Video> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Video::getCourseId,id);
List<Video> videoList = this.list(wrapper);
for (Video video : videoList) {
String videoSourceId = video.getVideoSourceId();
//如果视频id不为空,调用方法删除
if(!StringUtils.isEmpty(videoSourceId)) {
tencentCloudVodService.removeTencentCloudVideo(videoSourceId);
}
}
this.remove(wrapper);
}
@Override
public void removeVideoById(Long id) {
Video video = this.getById(id);
String videoSourceId = video.getVideoSourceId();
tencentCloudVodService.removeTencentCloudVideo(videoSourceId);
this.removeById(video);
}
@Override
public void updateVideoById(Video video) {
Video oldVideo = this.getById(video.getId());
//如果新的文件id与旧的不相等
if(oldVideo.getVideoSourceId()!=null && !oldVideo.getVideoSourceId().equals(video.getVideoSourceId())){
//先删除旧视频
tencentCloudVodService.removeTencentCloudVideo(oldVideo.getVideoSourceId());
}
this.updateById(video);
}
}
TencentCloudVodService
package com.atguigu.ggkt.vod.service;
import java.io.InputStream;
/**
* @Created with IntelliJ IDEA.
* @author:YanHui
* @date: 2023/01/05/19:33
* @msg:
*/
public interface TencentCloudVodService {
/**
* 删除云点播视频
* @param fileId
*/
void removeTencentCloudVideo(String fileId);
}
TencentCloudVodServiceImpl
package com.atguigu.ggkt.vod.service.impl;
import com.atguigu.ggkt.exception.GgktException;
import com.atguigu.ggkt.vod.service.TencentCloudVodService;
import com.atguigu.ggkt.vod.utils.ConstantPropertiesUtil;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaRequest;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaResponse;
import org.springframework.stereotype.Service;
import java.io.InputStream;
/**
* @Created with IntelliJ IDEA.
* @author:YanHui
* @date: 2023/01/05/19:36
* @msg:
*/
@Service
public class TencentCloudVodServiceImpl implements TencentCloudVodService {
@Override
public void removeTencentCloudVideo(String fileId) {
try{
// 实例化一个认证对象,入参需要传入腾讯云账户 SecretId 和 SecretKey,此处还需注意密钥对的保密
// 代码泄露可能会导致 SecretId 和 SecretKey 泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议采用更安全的方式来使用密钥,请参见:https://cloud.tencent.com/document/product/1278/85305
// 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
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(fileId);
// 返回的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,e.getMessage());
}
}
}