官方文档

前言

所谓视频上传,是指开发者或其用户将视频文件上传到点播的视频存储中,以便进行视频处理、分发等。

一、简介

腾讯云点播支持如下几种视频上传方式:

  1. 控制台上传:在点播控制台上进行操作,将本地视频上传到云点播,适用于直接管理少量视频的场景,具有方便快捷、无技术门槛的优点;
  2. 服务端上传:开发者将存储在其后台服务器中的视频上传到云点播,适用于自动化、系统化的运营场景;
  3. 客户端上传:终端用户将客户端本地视频上传到云点播,适用于 UGC、PGC 等场景,支持如下三端:

本文将介绍第三种视频上传方式: Web客户端上传

所谓客户端视频上传,是指 App 的最终用户将本地视频上传到点播平台。客户端上传的整体流程如下图所示:

二、思路

  1. 开通服务
  2. 获取云 API 密钥
  3. 服务端派发签名
  4. 客户端集成

说明: 前2步参照腾讯云官方文档指引自己完成, 本文着重讲解后2步代码实现

三、最终效果

四、代码实现

1、服务端派发签名

首先, 为什么要这一步, 因为, 在客户端上传场景下,客户端是直接将视频文件上传到腾讯云点播,不需要由 App 服务端进行中转。因此,腾讯云点播必须对发起请求的客户端进行鉴权。但由于 SecretKey 的权限过大,App 不应该将此信息泄露到客户端,否则将会造成严重的安全问题。

因此,客户端在发起上传之前,必须要到 App 的签名派发服务申请上传签名,即流程图中的第1步。

2、获取签名代码

获取签名一共只需要两个类: SignatureSignatureController

1)、Signature

  1. package com.cn.pinliang.admin.media.upload;
  2. import sun.misc.BASE64Encoder;
  3. import javax.crypto.Mac;
  4. import javax.crypto.spec.SecretKeySpec;
  5. public class Signature {
  6. private String secretId;
  7. private String secretKey;
  8. private long currentTime;
  9. private int random;
  10. private int signValidDuration;
  11. private static final String HMAC_ALGORITHM = "HmacSHA1";
  12. private static final String CONTENT_CHARSET = "UTF-8";
  13. public static byte[] byteMerger(byte[] byte1, byte[] byte2) {
  14. byte[] byte3 = new byte[byte1.length + byte2.length];
  15. System.arraycopy(byte1, 0, byte3, 0, byte1.length);
  16. System.arraycopy(byte2, 0, byte3, byte1.length, byte2.length);
  17. return byte3;
  18. }
  19. public String getUploadSignature() throws Exception {
  20. String strSign;
  21. String contextStr = "";
  22. long endTime = (currentTime + signValidDuration);
  23. contextStr += "secretId=" + java.net.URLEncoder.encode(secretId, "utf8");
  24. contextStr += "&currentTimeStamp=" + currentTime;
  25. contextStr += "&expireTime=" + endTime;
  26. contextStr += "&random=" + random;
  27. try {
  28. Mac mac = Mac.getInstance(HMAC_ALGORITHM);
  29. SecretKeySpec secretKey = new SecretKeySpec(this.secretKey.getBytes(CONTENT_CHARSET), mac.getAlgorithm());
  30. mac.init(secretKey);
  31. byte[] hash = mac.doFinal(contextStr.getBytes(CONTENT_CHARSET));
  32. byte[] sigBuf = byteMerger(hash, contextStr.getBytes("utf8"));
  33. strSign = new String(new BASE64Encoder().encode(sigBuf).getBytes());
  34. strSign = strSign.replace(" ", "").replace("\n", "").replace("\r", "");
  35. } catch (Exception e) {
  36. throw e;
  37. }
  38. return strSign;
  39. }
  40. public void setSecretId(String secretId) {
  41. this.secretId = secretId;
  42. }
  43. public void setSecretKey(String secretKey) {
  44. this.secretKey = secretKey;
  45. }
  46. public void setCurrentTime(long currentTime) {
  47. this.currentTime = currentTime;
  48. }
  49. public void setRandom(int random) {
  50. this.random = random;
  51. }
  52. public void setSignValidDuration(int signValidDuration) {
  53. this.signValidDuration = signValidDuration;
  54. }
  55. }

2)、SignatureController

  1. package com.cn.pinliang.admin.controller;
  2. import com.cn.pinliang.JsonResult;
  3. import com.cn.pinliang.admin.media.upload.Signature;
  4. import com.ctt.framework.ConfigUtil;
  5. import com.google.common.collect.Maps;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.web.bind.annotation.RequestMapping;
  8. import org.springframework.web.bind.annotation.ResponseBody;
  9. import java.util.Map;
  10. import java.util.Random;
  11. @Controller
  12. @RequestMapping("/signature")
  13. public class SignatureController{
  14. @RequestMapping("getUgcUploadSign")
  15. @ResponseBody
  16. public JsonResult getUgcUploadSign() {
  17. String secretId = "your secretId";
  18. String secretKey = "your secretKey";
  19. Signature sign = new Signature();
  20. sign.setSecretId(secretId);
  21. sign.setSecretKey(secretKey);
  22. sign.setCurrentTime(System.currentTimeMillis() / 1000);
  23. sign.setRandom(new Random().nextInt(java.lang.Integer.MAX_VALUE));
  24. sign.setSignValidDuration(3600 * 24 * 2);
  25. try {
  26. String signature = sign.getUploadSignature();
  27. Map<String, String> map = Maps.newHashMap();
  28. map.put("signature", signature);
  29. return JsonResult.succeed(map);
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. return JsonResult.fail("获取签名失败");
  33. }
  34. }
  35. }

好了, 现在可以调用getUgcUploadSign获取签名了

3、客户端集成

1)、说明

说是客户端集成, 其实就是前端js+css+html实现视频的上传, 看了官方文档和GitHub demo, 发现有一个问题, 那就是难道每次上传视频都要copy这些代码到我们自己的页面吗, 能不能抽离出单独的js直接调用就行了? 可以, 但是:

所以, 我们不能直接剥离这些js, 还得封装一下才行

2)、封装(干货才开始)

先看下经过后面代码的封装上传视频有多么简单, 真正做到一行代码解决视频加载与上传:

loadAndUploadVideo('form_id', 'teachingVideo', "uploadVideo", "videoFileId", 200);

  • 首先, 既然要用别人的SDK, 我们得引入相关依赖才行, 在jsp页面引入:
  1. <%--腾讯云上传视频js依赖--%>
  2. <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>
  3. <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.21/vue.js"></script>
  4. <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
  5. <script src="https://unpkg.com/vod-js-sdk-v6"></script>
  6. <%--上传进度div--%>
  7. <div class="center-in-center" id="txUploadProgress" style="display: none"></div>

说明:

a. 官方文档用的是vue.js, 虽然没学过, 但好歹有点js基础, 万变不离其宗, 不会的查下相关文档就行了

b. 与官方demo上传进度有点不一样的是, 我在上传时加了一层遮罩层, 页面中心显示上传进度, 所以引入了上传进度div, 总之, 根据自己实际项目需要来

  • 然后, 在你的页面嵌入:
  1. <div id="uploadVideo" style="margin-left: 40px; margin-top: 20px">
  2. <input type="hidden" id="teachingVideo" value="${hotProduct.teachingVideo}"/>
  3. <form ref="vExample">
  4. <input type="file" style="display:none;" ref="vExampleFile" @change="vExampleUpload($event)" id="videoFileId" accept="video/mp4"/>
  5. </form>
  6. <div class="btn btn-app btn-default btn-sm pr" style="width: 160px; ">
  7. <i @click="vExampleAdd" class="ace-icon fa fa-cloud-upload bigger-200" style="font-size: 20px">上传视频</i>
  8. </div>
  9. </div>

说明:

调用时指定的id与此页面元素id一一对应

  • 然后, 新建js文件, 代码:
  1. /**
  2. * 腾讯云视频上传
  3. */
  4. var progressId = 'txUploadProgress';// 上传进度id
  5. /**
  6. * 加载和上传视频: 页面初始化时调用, 可以直接通过视频url加载视频到页面
  7. * 除maxSize字段非必传外, 其他字段均必传, 否则后果自负
  8. * @param formId form表单id
  9. * @param name 与后端绑定的name字段, 如shortVideo字段, 此字段必须与隐藏域id字段相同, 即id name一致
  10. * @param uploadVideoDivId 视频上传相关的div块id
  11. * @param videoFileId 视频文件id, 此字段作用是解决vue无法再次上传相同文件的问题, 将此id值清空即可再次上传
  12. * @param maxSize 文件大小, M为单位, 不传表示不限制文件大小
  13. */
  14. function loadAndUploadVideo(formId, name, uploadVideoDivId, videoFileId, maxSize) {
  15. loadVideo(uploadVideoDivId, name);
  16. uploadVideo(formId, name, uploadVideoDivId, videoFileId, maxSize)
  17. }
  18. /**
  19. * 加载视频
  20. * @param uploadVideoDivId 将视频追加到此id后面
  21. * @param name 根据name获取原视频值, 因此id与name字段必须相同才能拿到旧值
  22. */
  23. function loadVideo(uploadVideoDivId, name) {
  24. var oldVideoUrl = $("#" + name).val();
  25. if (oldVideoUrl != null && oldVideoUrl != ""){
  26. var str = '';
  27. str += '<div class="video">' ;
  28. str += ' <video src="'+oldVideoUrl+'" controls="controls" >';
  29. str += '</div>';
  30. $("#" + uploadVideoDivId + " .video").remove();
  31. $("#" + uploadVideoDivId).append(str);
  32. }
  33. }
  34. /**
  35. * 上传视频, 腾讯云官方demo + 改造
  36. * @param formId
  37. * @param name
  38. * @param uploadVideoDivId
  39. * @param videoFileId
  40. * @param maxSize 文件大小, M为单位, 不传表示不限制文件大小
  41. */
  42. function uploadVideo(formId, name, uploadVideoDivId, videoFileId, maxSize) {
  43. // 获取签名, 腾讯云要求直接上传视频的客户端必须获取签名
  44. function getSignature() {
  45. var url = "" + ctx + "signature/getUgcUploadSign.action"
  46. return axios.post(url).then(function (response) {
  47. return response.data.data.signature
  48. })
  49. };
  50. var app = new Vue({
  51. el: '#' + uploadVideoDivId,
  52. data: {
  53. uploaderInfos: [],
  54. },
  55. created: function () {
  56. this.tcVod = new TcVod.default({
  57. getSignature: getSignature
  58. })
  59. },
  60. methods: {
  61. vExampleAdd: function () {
  62. this.$refs.vExampleFile.click()
  63. },
  64. vExampleUpload: function (event) {
  65. if (!checkVideo(event, maxSize, videoFileId)) {
  66. return;
  67. }
  68. onVideoSelected()
  69. var self = this;
  70. var videoFile = this.$refs.vExampleFile.files[0]
  71. var uploader = this.tcVod.upload({
  72. videoFile: videoFile,
  73. })
  74. uploader.on('video_progress', function (info) {
  75. uploaderInfo.progress = info.percent;
  76. // 上传进度
  77. var percent = Math.floor(uploaderInfo.progress * 100) + '%'
  78. $("#" + progressId).text("正在上传 : " + percent)
  79. })
  80. uploader.on('video_upload', function (info) {
  81. uploaderInfo.isVideoUploadSuccess = true;
  82. })
  83. var uploaderInfo = {
  84. videoInfo: uploader.videoInfo,
  85. isVideoUploadSuccess: false,
  86. isVideoUploadCancel: false,
  87. progress: 0,
  88. fileId: '',
  89. videoUrl: '',
  90. cancel: function() {
  91. uploaderInfo.isVideoUploadCancel = true;
  92. uploader.cancel()
  93. },
  94. }
  95. this.uploaderInfos.push(uploaderInfo)
  96. uploader.done().then(function(doneResult) {
  97. uploaderInfo.fileId = doneResult.fileId;
  98. return doneResult.video.url;
  99. }).then(function (videoUrl) {
  100. uploaderInfo.videoUrl = videoUrl
  101. onVideoUploaded(formId, name, videoUrl, videoFileId, uploadVideoDivId);
  102. })
  103. }
  104. },
  105. })
  106. }
  107. /**
  108. * 选择视频后显示上传进度
  109. */
  110. function onVideoSelected() {
  111. $("#" + progressId).text("正在上传 : 0%")
  112. $("#" + progressId).show()
  113. startLoadding();
  114. }
  115. /**
  116. * 视频上传完成
  117. * @param formId
  118. * @param name
  119. * @param videoUrl
  120. * @param videoFileId
  121. * @param uploadVideoDivId
  122. */
  123. function onVideoUploaded(formId, name, videoUrl, videoFileId, uploadVideoDivId) {
  124. endLoadding();
  125. $("#" + progressId).hide();
  126. layerAlert("上传完成")
  127. // 上传完成后清空fileId, 否则vue无法再次选择此文件
  128. $("#" + videoFileId).val('')
  129. // 将上传成功后的视频url追加到表单
  130. formAppendVideo(formId, name, videoUrl);
  131. if (videoUrl != ""){
  132. var str = '';
  133. str += '<div class="video">';
  134. str += ' <video src="'+videoUrl+'" controls="controls" >';
  135. str += '</div>';
  136. $("#" + uploadVideoDivId + " .video").remove();
  137. $("#" + uploadVideoDivId).append(str);
  138. }
  139. }
  140. /**
  141. * 删除视频, 删除页面视频只需要uploadVideoDivId字段即可, 但同时还应清空form表单的videoUrl
  142. * @param formId
  143. * @param uploadVideoDivId
  144. * @param name
  145. */
  146. function deleteVideo(formId, uploadVideoDivId, name) {
  147. $("#" + uploadVideoDivId + " .video").remove();
  148. formAppendVideo(formId, name, null);
  149. }
  150. /**
  151. * 追加视频url到form
  152. * @param formId
  153. * @param name
  154. * @param videoUrl
  155. */
  156. function formAppendVideo(formId, name, videoUrl){
  157. videoUrl = null == videoUrl ? '' : videoUrl;
  158. var form = $('#' + formId);
  159. var tmpInput = $('<input type="hidden" name="'+name+'" value="'+videoUrl+'" />');
  160. form.append(tmpInput);
  161. }
  162. /**
  163. * 校验视频格式和大小
  164. * @param event
  165. * @param maxSize 不传表示不限制文件大小
  166. * @returns {boolean}
  167. */
  168. function checkVideo(event, maxSize, videoFileId) {
  169. var flag = true;
  170. var accept = event.target.accept;
  171. var file = event.target.files[0];
  172. var type = file.type
  173. if(accept.indexOf(type) == -1) {
  174. layerAlert('文件格式不正确');
  175. $("#" + videoFileId).val('')
  176. flag = false;
  177. }
  178. if (maxSize != undefined) {
  179. if(file.size > 1024 * 1024 * maxSize) {
  180. layerAlert('文件不能大于' + maxSize + 'M');
  181. $("#" + videoFileId).val('')
  182. flag = false;
  183. }
  184. }
  185. return flag
  186. }

说明:

a. 代码注释足够详细了

b. 视频上传核心代码在uploadVideo方法

稍微解释下uploadVideo方法:

  • 先调用SignatureController里面的getUgcUploadSign方法获取签名
  • 再通过vue定义视频文件点击、选择、上传后的事件

c. 有人会问不是上传视频吗, 为什么还有loadVideo, 因为, 编辑时进入页面不可能之前的视频就不显示吧, 所以, 初始化调用loadAndUploadVideo方法时就会将原视频嵌入到页面中了

d. 本文还加了上传视频的一些限制, 比如格式、大小之类的

e. formAppendVideo方法需要说明一下, 这个方法作用是上传视频后直接将视频url以name形式追加到表单元素, 这样就不用再form submit的时候再获取videoUrl了, 直接提交到后端, 这也是一行代码搞定视频上传的必要方法

3)、使用

上述代码就可以实现一行代码搞定视频上传加载了, 通过在页面初始化时调用loadAndUploadVideo方法即可, 具体如下:

a: jsp页面引入相关vue js

b: jsp页面嵌入html

c: 新建js, 将上述js代码copy进去

d: 页面加载完毕初始化时调用:

loadAndUploadVideo('form_id', 'teachingVideo', "uploadVideo", "videoFileId", 200);即可

结语: 本文通过参考腾讯云点播官方文档, 实现了对视频上传和加载功能的封装, 最终能够通过调用一行代码实现功能, 由于每个项目不同, 小伙伴需要根据自己实际情况修改进行代码适配, 谢谢

腾讯云点播视频存储(Web端视频上传)的更多相关文章

  1. 腾讯云COS对象存储 Web 端直传实践(JAVA实现)

    使用 腾讯云COS对象存储做第三方存储云服务,把一些文件都放在上面,这里主要有三中实现方式:第一种就是在控制台去设置好,直接上传文件.第二种就是走服务端,上传文件,就是说,上传文件是从服务端去上传上去 ...

  2. 如何利用京东云的对象存储(OSS)上传下载文件

    作者:刘冀 在公有云厂商里都有对象存储,京东云也不例外,而且也兼容S3的标准因此可以利用相关的工具去上传下载文件,本文主要记录一下利用CloudBerry Explorer for Amazon S3 ...

  3. python django web 端文件上传

    利用Django实现文件上传并且保存到指定路径下,其实并不困难,完全不需要用到django的forms,也不需要django的models,就可以实现,下面开始实现. 第一步:在模板文件中,创建一个f ...

  4. web端文件上传,预览,下载,删除

      //HTML部分 <div class="item attachment attachmentNew"> <span class="name&quo ...

  5. JavaWeb-SpringBoot_(下)腾讯云点播服务之视频的显示-demo

    腾讯视频云点播 传送门 项目在腾讯云点播服务之视频的上传(上)[附源码]的基础上添加了两个html页面 此视频  播放传送门 (播放视频GIF会超过10M...) package com.Gary.v ...

  6. JavaWeb-SpringBoot_(上)腾讯云点播服务之视频的上传-demo

    使用Gradle编译项目 传送门 腾讯视频云点播 传送门 项目已托管到Github上 传送门 腾讯云点播服务之视频的显示(下) 传送门 个人腾讯云控制台中的视频管理 IndexController.j ...

  7. web端视频直播网站的弊端和优势

    在YY上市前后,国内涌出一批类YY视频直播或9158的秀场类网站. 比如六间房,酷六等等 这种web端视频服务基本依靠web本身的特性,用flash直播,靠CDN提供服务. 但是这样的架构有2个问题 ...

  8. 腾讯云 COS 对象存储使用

    目前使用腾讯云的对象存储cos服务,将本地的文件同步到cos中,看了腾讯云的用户文档,发现使用COS Migration 工具还是挺适合的. 原因 因为服务器已经安装有java环境,而cos的几个用户 ...

  9. 腾讯云Redis混合存储版重磅推出,万字长文助你破解缓存难题!

    导语 | 缓存+存储的系统架构是目前常见的系统架构,缓存层负责加速访问,存储层负责存储数据.这样的架构需要业务层或者是中间件去实现缓存和存储的双写.冷热数据的交换,同时还面临着缓存失效.缓存刷脏.数据 ...

随机推荐

  1. C# String字符串

    C#(静态String类) C#中提供了比较全面的字符串处理方法,很多函数都进行了封装为我们的编程工作提供了很大的便利.System.String是最常用的字符串操作类,可以帮助开发者完成绝大部分的字 ...

  2. Keil_uvision 基本使用教程

    Keil C51 V9.00 即09年发布的最新版本uVision 4,版本外观改变比较大,可以使用以前的注册文件.如果全新安装,在VISTA或者WIN 7系统下,请使用管理员方式运行,然后注册即可无 ...

  3. 关于python logging的 NOTSET 级别

    说重点: NOTSET 意指不设置 所以按照父logger级别来过滤日志 注意 不是最低级别的意思 由于logging中root日志对象的默认级别是WARNING, 所以当你使用logging.get ...

  4. Naive Bayes 笔记

    Naive Bayes (朴素贝叶斯) 属于监督学习算法, 它通过计算测试样本在训练样本各个分类中的概率来确定测试样本所属分类, 取最大概率为其所属分类.  优点  在数据较少的情况下仍然有效,可以处 ...

  5. DXP常用有效的快捷操作记录

    1.在PCB中快速选中一个器件 1)  M+C+Enter将弹出元件对话框,移动一个元件后,在十字架光标 状态时按[Enter]键 2)M(Move)+M(Move)按下时,鼠标光标变成“+”后,点击 ...

  6. UNIGUI换版本注意事项

    比如UNIGUI换版本注意事项 许多人在更换UNIGUI版本时,会遇到各种问题,报各样错.比如下面的: 然后便不知所措,怀疑是UNIGUI新版本有问题——不能安装成功.其实不然. 下面是正确的解决方法 ...

  7. (leetcode162)find peak element

    1题目 A peak element is an element that is greater than its neighbors. Given an input array where num[ ...

  8. [NewCode 6] 重建二叉树

    题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字.例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7, ...

  9. AEAI WM v1.6.0 升级说明,开源工作管理系统

    1 升级说明 AEAI WM v1.6.0版是AEAI WM v1.5.0版工作管理系统的升级版本,本次升级的系统是基于AEAI DP 3.8.0_20170228进行打包部署的,对产品中的功能及BU ...

  10. 关于css3中的flex

    参考几篇文章: Flex 布局语法教程 IE10中的Flexible Box("Flexbox")布局 “老”的Flexbox和“新”的Flexbox 一个可以练习的地方: NEW ...