腾讯云视频点播-web上传视频 环境debian 13\php8.3\thinkphp8.1; 把上传后的视频及封面数据保存到mysql8.4中表tp_video结构CREATE TABLE tp_video ( id bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 主键ID, file_id varchar(32) NOT NULL COMMENT 腾讯云VOD FileId, video_url varchar(1000) NOT NULL DEFAULT COMMENT 视频地址, video_verify_content varchar(1000) NOT NULL DEFAULT COMMENT 视频校验内容, cover_url varchar(1000) NOT NULL DEFAULT COMMENT 封面地址, cover_verify_content varchar(1000) NOT NULL DEFAULT COMMENT 封面校验内容, status tinyint unsigned NOT NULL DEFAULT 1 COMMENT 状态0禁用 1正常, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_file_id (file_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT腾讯云点播视频表;客户端代码!DOCTYPE html html head meta charsetutf-8 meta http-equivContent-Type contenttext/html; charsetUTF-8 / meta http-equivX-UA-Compatible contentIEedge meta nameviewport contentwidthdevice-width, initial-scale1 titleQCloud VIDEO UGC UPLOAD SDK/title link href//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css relstylesheet style typetext/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 idcontent div classcontainer h1UGC-Uploader/h1 /div /div div classcontainer idmain-area div classrow stylepadding:10px; p 1.示例中的签名是直接从demo后台获取签名。该 demo 对应的点播账号已开启防盗链功能此视频分发 URL 仅限 2 个独立 IP 进行访问有效期为 10 分钟。br 2.示例1点击“直接上传视频”按钮即可上传视频。br 3.示例2点击“添加视频”添加视频文件点击“添加封面”添加封面文件然后点击“上传视频和封面”按钮即可上传视频和封面。br 4.取消上传为取消上传中的视频上传成功的视频不能取消上传。 /p /div form refvExample input typefile styledisplay:none; refvExampleFile changevExampleUpload / /form div classrow stylepadding:10px; h4示例1直接上传视频/h4 a hrefjavascript:void(0); classbtn btn-default clickvExampleAdd直接上传视频/a /div form refvcExample input typefile styledisplay:none; refvcExampleVideo changesetVcExampleVideoName() / input typefile styledisplay:none; refvcExampleCover changesetVcExampleCoverName() / /form div classrow stylepadding:10px; h4示例2上传视频和封面/h4 a hrefjavascript:void(0); classbtn btn-default clickvcExampleAddVideo{{vcExampleVideoName || 添加视频}}/a a hrefjavascript:void(0); classbtn btn-default clickvcExampleAddCover{{vcExampleCoverName || 添加封面}}/a a hrefjavascript:void(0); classbtn btn-default clickvcExampleAddUpload上传视频和封面/a /div form refcExample input typefile styledisplay:none; refcExampleCover changecExampleUpload / /form div classrow idresultBox !-- 上传信息组件 -- div classuploaderMsgBox v-foruploaderInfo in uploaderInfos div v-ifuploaderInfo.videoInfo 视频名称{{uploaderInfo.videoInfo.name . uploaderInfo.videoInfo.type}} 上传进度{{Math.floor(uploaderInfo.progress * 100) %}} fileId{{uploaderInfo.fileId}} 上传结果{{uploaderInfo.isVideoUploadCancel ? 已取消 : uploaderInfo.isVideoUploadSuccess ? 上传成功 : 上传中}} br 地址{{uploaderInfo.videoUrl}} a hrefjavascript:void(0); classcancel-upload v-if!uploaderInfo.isVideoUploadSuccess !uploaderInfo.isVideoUploadCancel clickuploaderInfo.cancel()取消上传/abr /div div v-ifuploaderInfo.coverInfo 封面名称{{uploaderInfo.coverInfo.name}} 上传进度{{Math.floor(uploaderInfo.coverProgress * 100) %}} 上传结果{{uploaderInfo.isCoverUploadSuccess ? 上传成功 : 上传中}} br 地址{{uploaderInfo.coverUrl}} br /div /div /div /div script src//cdn.jsdelivr.net/npm/es6-promise4/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//cdn-go.cn/cdn/vod-js-sdk-v6/latest/vod-js-sdk-v6.js/script script typetext/javascript ;(function () { /** * 计算签名 */ function getSignature() { return axios.post(/api/tencent/index/).then(function (response) { return response.data.signature; }) } /** * 保存腾讯云上传结果到自己的数据库 */ function saveUploadResult(doneResult) { return axios.post(/api/tencent/saveVideo/, doneResult) } var app new Vue({ el: #main-area, data: { uploaderInfos: [], vcExampleVideoName: , vcExampleCoverName: , cExampleFileId: , }, created: function () { this.tcVod new TcVod.default({ getSignature: getSignature }) }, methods: { /** * 示例1 * 添加视频 */ vExampleAdd: function () { this.$refs.vExampleFile.click() }, /** * 示例1 * 直接上传视频 */ vExampleUpload: function () { var self this; var mediaFile this.$refs.vExampleFile.files[0]; if (!mediaFile) { return; } var uploader this.tcVod.upload({ mediaFile: mediaFile, }); var uploaderInfo { videoInfo: uploader.videoInfo, isVideoUploadSuccess: false, isVideoUploadCancel: false, progress: 0, fileId: , videoUrl: , cancel: function () { uploaderInfo.isVideoUploadCancel true; uploader.cancel(); }, }; /** * 上传进度 */ uploader.on(media_progress, function (info) { uploaderInfo.progress info.percent; }); /** * 视频文件上传完成 */ uploader.on(media_upload, function (info) { uploaderInfo.isVideoUploadSuccess true; }); console.log(uploader, uploader); this.uploaderInfos.push(uploaderInfo); /** * 腾讯云整个上传流程完成 */ uploader.done().then(function (doneResult) { console.log(doneResult, doneResult); /** * 原有功能保留 */ uploaderInfo.fileId doneResult.fileId; uploaderInfo.videoUrl doneResult.video.url; /** * 保存到自己的 MySQL */ return saveUploadResult(doneResult); }).then(function (response) { console.log(数据库保存结果, response.data); if (response.data.code ! 0) { console.error(数据库保存失败, response.data.msg); } /** * 原有重置功能保留 */ self.$refs.vExample.reset(); }).catch(function (error) { console.error(视频上传或数据库保存失败, error); }); }, /** * 示例2 * 选择视频 */ setVcExampleVideoName: function () { var file this.$refs.vcExampleVideo.files[0]; if (file) { this.vcExampleVideoName file.name; } }, /** * 示例2 * 选择封面 */ setVcExampleCoverName: function () { var file this.$refs.vcExampleCover.files[0]; if (file) { this.vcExampleCoverName file.name; } }, /** * 示例2 * 添加视频 */ vcExampleAddVideo: function () { this.$refs.vcExampleVideo.click() }, /** * 示例2 * 添加封面 */ vcExampleAddCover: function () { this.$refs.vcExampleCover.click() }, /** * 示例2 * 上传视频 封面 */ vcExampleAddUpload: function () { var self this; var mediaFile this.$refs.vcExampleVideo.files[0]; var coverFile this.$refs.vcExampleCover.files[0]; if (!mediaFile) { console.error(请选择视频文件); return; } if (!coverFile) { console.error(请选择封面文件); return; } var uploader this.tcVod.upload({ mediaFile: mediaFile, coverFile: coverFile, }); var uploaderInfo { videoInfo: uploader.videoInfo, coverInfo: uploader.coverInfo, isVideoUploadSuccess: false, isVideoUploadCancel: false, isCoverUploadSuccess: false, progress: 0, coverProgress: 0, fileId: , videoUrl: , coverUrl: , cancel: function () { uploaderInfo.isVideoUploadCancel true; uploader.cancel(); }, }; /** * 视频上传进度 */ uploader.on(media_progress, function (info) { uploaderInfo.progress info.percent; }); /** * 视频文件上传完成 */ uploader.on(media_upload, function (info) { uploaderInfo.isVideoUploadSuccess true; }); /** * 封面上传进度 */ uploader.on(cover_progress, function (info) { uploaderInfo.coverProgress info.percent; }); /** * 封面文件上传完成 */ uploader.on(cover_upload, function (info) { uploaderInfo.isCoverUploadSuccess true; }); console.log(uploader, uploader); this.uploaderInfos.push(uploaderInfo); /** * 腾讯云上传完成 */ uploader.done().then(function (doneResult) { console.log(doneResult, doneResult); /** * 原有功能全部保留 */ uploaderInfo.fileId doneResult.fileId; if (doneResult.video) { uploaderInfo.videoUrl doneResult.video.url; } if (doneResult.cover) { uploaderInfo.coverUrl doneResult.cover.url; } /** * 把腾讯云返回结果保存数据库 */ return saveUploadResult(doneResult); }).then(function (response) { console.log(数据库保存结果, response.data); if (response.data.code ! 0) { console.error(数据库保存失败, response.data.msg); } /** * 原有重置功能保留 */ self.$refs.vcExample.reset(); self.vcExampleVideoName ; self.vcExampleCoverName ; }).catch(function (error) { console.error(上传或数据库保存失败, error); }); }, /** * 示例3 * 添加封面 */ cExampleAddCover: function () { this.$refs.cExampleCover.click() }, /** * 示例3 * 为已经存在的 fileId 上传封面 */ cExampleUpload: function () { var self this; var coverFile this.$refs.cExampleCover.files[0]; if (!coverFile) { console.error(请选择封面); return; } if (!this.cExampleFileId) { console.error(请输入 fileId); return; } var uploader this.tcVod.upload({fileId: this.cExampleFileId, coverFile: coverFile}); var uploaderInfo { coverInfo: uploader.coverInfo, isCoverUploadSuccess: false, coverProgress: 0, coverUrl: , cancel: function () { uploader.cancel(); }, }; /** * 封面上传进度 */ uploader.on( cover_progress, function (info) { uploaderInfo.coverProgress info.percent; } ); /** * 封面上传成功 */ uploader.on( cover_upload, function (info) { uploaderInfo.isCoverUploadSuccess true; } ); console.log(uploader, uploader); this.uploaderInfos.push(uploaderInfo); uploader.done().then(function (doneResult) { console.log(doneResult,doneResult); if (doneResult.cover) { uploaderInfo.coverUrl doneResult.cover.url; } /** * 注意 * 单独上传封面返回的数据结构 * 可能没有 video。 * * 我们补上 fileId * 方便 TP8 更新数据库。 */ doneResult.fileId self.cExampleFileId; /** * 保存/更新数据库 */ return saveUploadResult(doneResult); }).then(function (response) { console.log(封面数据库更新结果, response.data); self.$refs.cExample.reset(); }).catch(function (error) { console.error(封面上传或保存失败, error); }); }, }, }); })(); /script /body /html后端上传签名?php declare (strict_types 1); namespace app\api\controller; use think\response\Json; use think\response\View; class Tencent { /** * 显示资源列表 * * return \think\Response */ public function index(): Json { // 确定 App 的云 API 密钥 $secret_id 你的ID; $secret_key 你的key; // 确定签名的当前时间和失效时间 $current time(); $expired $current 86400; // 签名有效期1天 // 向参数列表填入参数 $arg_list array( secretId $secret_id, currentTimeStamp $current, expireTime $expired, random rand()); // 计算签名 $original http_build_query($arg_list); $signature base64_encode(hash_hmac(SHA1, $original, $secret_key, true).$original); return json([signature $signature]); } public function uploadVideo(): View { return view(/tencent-upload); } public function saveVideo(): Json { if (!request()-isPost()) { return json([code 1, msg 非法请求]); } try { $data request()-post(); if (empty($data)) { $data json_decode(request()-getContent(), true); } if (!is_array($data)) { return json([code 1, msg JSON数据格式错误]); } $fileId trim((string) ($data[fileId] ?? )); if ($fileId ) { return json([code 1, msg fileId不能为空]); } $saveData [ video_url (string)($data[video][url] ?? ), video_verify_content (string)($data[video][verify_content] ?? ), cover_url (string)($data[cover][url] ?? ), cover_verify_content (string)($data[cover][verify_content] ?? ), status 1, updated_at date(Y-m-d H:i:s), ]; /* * 查询 fileId */ $id Db::name(video)-where(file_id, $fileId)-value(id); if ($id) { /* * 已存在 → 更新 */ Db::name(video)-where(id, $id)-update($saveData); } else { /* * 不存在 → 新增 */ $saveData[file_id] $fileId; $saveData[created_at] date(Y-m-d H:i:s); $id Db::name(video)-insertGetId($saveData); } return json([ code 0, msg 保存成功, data [ id $id, file_id $fileId, video_url $saveData[video_url], cover_url $saveData[cover_url], ] ]); } catch (\Throwable $e) { return json([code 1, msg 保存失败 . $e-getMessage()]); } } }

相关新闻

最新新闻

UVC消毒笔嵌入Nordic BLE SoC的完整工程复盘

UVC消毒笔嵌入Nordic BLE SoC的完整工程复盘

UVC消毒笔这个品类,前两年市场需求突然就起来了,各路方案商、品牌方、初创团队都往里冲。当时市面上大多数产品还停留在“一颗电池一个升压板一颗灯珠”的阶段,讲究点的加个机械拨杆开关,没了。而“UVC Disinfecting Pen Embeds N…

2026/8/28 18:50:26
Buy My Agent MCP Server:AI Agent搜索与发现工具部署指南

Buy My Agent MCP Server:AI Agent搜索与发现工具部署指南

MCP 生态最近越来越热闹,但大多数项目是“做一个 MCP Server”,真正解决“我怎么找到想要的 Agent”这个问题的项目不多。这次要看的这个项目很有意思,标题直接叫Buy My Agent MCP Server,发布在 Hacker News 的 Show HN 上。它做…

2026/8/28 18:50:26
先楫 HPM 与 STM32G491 CAN FD 通信延迟排查:从 4 个周期优化到同周期反馈

先楫 HPM 与 STM32G491 CAN FD 通信延迟排查:从 4 个周期优化到同周期反馈

先楫 HPM 与 STM32G491 CAN FD 通信延迟排查:从 4 个周期优化到同周期反馈 最近在人形机器人通信链路调试中,发现了一个比较典型、也比较容易被忽略的 CAN FD 时序问题。 先楫板周期下发控制指令,关节模组接收控制量、完成控制处理后&#xf…

2026/8/28 18:50:26
跨仓库代码上下文与Mid-Training:大模型长上下文代码建模实践

跨仓库代码上下文与Mid-Training:大模型长上下文代码建模实践

做大模型代码生成、代码补全或者仓库级代码理解时,很多同学会遇到一个很尴尬的瓶颈:单文件、单函数的上下文模型处理得很好,一旦把上下文拉长到跨文件、跨模块、甚至跨仓库,模型的推理能力就明显下降。近期看到 OctoLong 这类围绕…

2026/8/28 18:50:26
Python面试核心:从基础语法到内存管理与高级特性深度解析

Python面试核心:从基础语法到内存管理与高级特性深度解析

1. 从面试官视角看Python基础题的价值 最近帮团队面试了几轮初级和中级Python开发,发现一个挺有意思的现象:很多候选人简历上项目写得天花乱坠,框架、中间件、高并发张口就来,但一碰到最基础的Python语言特性题,回答就…

2026/8/28 18:50:26
从蓝桥杯国赛题解析物联网开发:STM32、LoRa与多任务调度实战

从蓝桥杯国赛题解析物联网开发:STM32、LoRa与多任务调度实战

1. 项目概述:从一道国赛题看物联网竞赛的核心能力 最近在整理过往的竞赛资料,翻到了第十一届蓝桥杯物联网赛项的国赛试题,感触颇深。这道题可以说是一个经典的缩影,它几乎涵盖了嵌入式物联网开发中所有核心且基础的环节&#xff1…

2026/8/28 18:45:26