当前位置: 首页 > news >正文

uni-app VOD 与 COS 选型、开发笔记

1. 项目背景与目标

在 uni-app 开发过程中,需要实现媒体文件(图片、视频)的上传功能,主要涉及腾讯云 VOD(视频点播)和 COS(对象存储)两种方案。

2. 技术选型与尝试过程

2.1 第一阶段:VOD SDK 尝试

2.1.1 初始方案:vod-js-sdk-v6

最初选择腾讯云 VOD SDK vod-js-sdk-v6 进行视频上传:

import TcVod from 'vod-js-sdk-v6'const tcVod = new TcVod({getSignature: () => {return new Promise((resolve, reject) => {uni.request({url: "后端签名接口",method: "POST",success: function (res) {const sig = res.data.data.signature;if (sig) {resolve(sig);} else {reject(new Error('签名不存在'));}}});});}
});
2.1.2 遇到的第一个错误
TypeError: Cannot read property 'userAgent' of undefined

问题分析vod-js-sdk-v6在初始化时会访问 navigator.userAgent,但在 uni-app 的某些运行环境中该对象不存在。

尝试解决

// 尝试模拟浏览器环境
const globalObj = (typeof window !== 'undefined') ? window : (typeof global !== 'undefined' ? global : this);
globalObj.navigator = globalObj.navigator || {};
globalObj.navigator.userAgent = globalObj.navigator.userAgent || 'app-plus';
2.1.3 遇到的第二个错误
文件查找失败:'axios' at node_modules\.store\vod-js-sdk-v6@1.7.1-beta.1\node_modules\vod-js-sdk-v6\lib\src\uploader.js:161
文件查找失败:'js-sha1' at node_modules\.store\vod-js-sdk-v6@1.7.1-beta.1\node_modules\vod-js-sdk-v6\lib\src\uploader.js:158

问题分析vod-js-sdk-v6依赖第三方库 axiosjs-sha1,但在 uni-app 环境中这些依赖无法正确加载。

尝试解决

npm install axios js-sha1
# 但仍存在兼容性问题

2.2 第二阶段:COS SDK 尝试

2.2.1 尝试 cos-js-sdk-v5

由于 VOD SDK 问题无法解决(后查找资料得知,vod只支持网页端),转向腾讯云 COS JavaScript SDK:

import COS from 'cos-js-sdk-v5'export default {data() {return {cos: null}},mounted() {this.initCOS();},methods: {initCOS() {this.cos = new COS({SecretId: 'your-secret-id',SecretKey: 'your-secret-key'});},async uploadToCOSDirect(file) {try {const result = await new Promise((resolve, reject) => {this.cos.putObject({Bucket: 'your-bucket-name-123456789',Region: 'your-region',Key: `uploads/${Date.now()}_${this.fileName}`,Body: file,  // 需要file类型的文件onProgress: (progressData) => {this.uploadProgress = progressData.percent;}}, (err, data) => {if (err) {reject(err);} else {resolve(data);}});});} catch (error) {console.error('上传失败:', error);}}}
}
2.2.2 遇到的错误
warning: cos-js-sdk-v5 不支持 nodejs 环境使用,请改用 cos-nodejs-sdk-v5

问题分析:在 uni-app App 端运行时,cos-js-sdk-v5 检测到 Node.js 环境而报错(可能uniapp打包过程是在node环境进行的)。

2.2.3 尝试 cos-nodejs-sdk-v5(静态导入方式)
npm uninstall cos-js-sdk-v5
npm install cos-nodejs-sdk-v5

修改导入方式:

import COS from 'cos-nodejs-sdk-v5'  // 静态导入方式export default {methods: {initCOS() {this.cos = new COS({SecretId: 'your-secret-id',SecretKey: 'your-secret-key'});}}
}
2.2.4 新的依赖错误
文件查找失败:'fast-xml-parser' at node_modules\.store\cos-nodejs-sdk-v5@2.15.4\node_modules\cos-nodejs-sdk-v5\sdk\util.js:9

问题分析cos-nodejs-sdk-v5依赖 ast-xml-parser,但在 uni-app 环境中无法正确加载。

2.3 第三阶段:COS SDK 动态导入方案

2.3.1 为什么要换成动态导入?

原因1:解决依赖加载问题

// 静态导入在编译时就会尝试加载所有依赖
import COS from 'cos-nodejs-sdk-v5'  // 编译时就可能报错// 动态导入在运行时才加载,可以更好地处理错误
let COS;
try {const cosModule = await import('cos-nodejs-sdk-v5');COS = cosModule.default || cosModule;
} catch (e) {console.error('加载失败:', e);
}

原因2:平台兼容性处理

// 不同平台可能需要不同的 SDK
async initCOS() {try {// 优先尝试 Node.js 版本const cosModule = await import('cos-nodejs-sdk-v5');COS = cosModule.default || cosModule;} catch (eImport) {try {// 备选 JavaScript 版本const cosModule = await import('cos-js-sdk-v5');COS = cosModule.default || cosModule;} catch (eRequire) {console.error('两个版本都加载失败');}}if (COS) {this.cos = new COS({SecretId: 'your-secret-id',SecretKey: 'your-secret-key'});}
}

原因3:按需加载,减少包体积

// 只在需要时才加载 SDK
methods: {async uploadToCOSDirect(file) {// 确保 COS SDK 已加载if (!this.cos) {await this.initCOS();}if (this.cos) {// 执行上传逻辑}}
}
2.3.2 动态导入实现方案
export default {data() {return {cos: null}},methods: {// 动态导入 COS SDKasync initCOS() {let COS;try {// 优先尝试动态 import(支持打包器)const mod = await import('cos-nodejs-sdk-v5');COS = mod && (mod.default || mod);} catch (eImport) {try {// fallback:require(某些编译环境可用)const req = require && require('cos-nodejs-sdk-v5');COS = req && (req.default || req);} catch (eRequire) {console.error('加载 cos-nodejs-sdk-v5 失败', eImport, eRequire);return;}}if (COS) {this.cos = new COS({SecretId: 'your-secret-id',SecretKey: 'your-secret-key'});}},// 使用动态导入的上传方法async uploadToCOSDirect(file) {try {// 确保 SDK 已加载if (!this.cos) {await this.initCOS();}if (!this.cos) {throw new Error('COS SDK 加载失败');}const result = await new Promise((resolve, reject) => {this.cos.putObject({Bucket: 'your-bucket',Region: 'ap-guangzhou',Key: `uploads/${Date.now()}_${file.name}`,Body: file,onProgress: (progressData) => {this.uploadProgress = progressData.percent;}}, (err, data) => {if (err) {reject(err);} else {resolve(data);}});});return result;} catch (error) {console.error('上传失败:', error);throw error;}}}
}

2.4 第四阶段:最终方案 - COS 预签名 URL

2.4.1 决策转变原因

基于以上多次尝试遇到的问题,决定转向更简单的 COS 预签名 URL 方式上传:

  1. 避免 SDK 兼容性问题:无需引入复杂的第三方 SDK
  2. 减少依赖冲突:不依赖额外的 npm 包
  3. 提高稳定性:使用 uni-app 原生 API 实现
  4. 简化实现:代码逻辑更清晰
2.4.2 最终实现方案
// 1. 获取预签名信息
const signatureRes = await new Promise((resolve, reject) => {uni.request({url: 'http://后端接口/uniAppDirectSign',method: 'POST',success: resolve,fail: reject});
});// 2. 使用 uni.uploadFile 上传
uni.uploadFile({url: 'https://' + signatureData.cosHost,filePath: filePath,name: 'file',formData: {'key': signatureData.cosKey,'policy': signatureData.policy,'q-sign-algorithm': signatureData.qSignAlgorithm,// ... 其他签名参数}
});

3. 最终实现详解

3.1 文件选择与上传流程

// 拍照功能
takePhoto() {uni.chooseImage({count: 1,sourceType: ['camera', 'album'],success: (res) => {const file = res.tempFiles[0];this.uploadToCOS(file); // 使用预签名URL上传}});
}

3.2 核心上传方法

1. 封装工具类,多处方便使用
class CosUploader {constructor() {this.baseUrl = 'your-address';}/*** 上传文件到腾讯云 COS* @param {Object} file - 要上传的文件对象* @param {Function} onProgress - 进度回调函数* @returns {Promise<string>} 返回文件访问 URL*/async uploadToCOS(file, onProgress) {if (!file) {throw new Error('请先选择文件');}try {const fileName = file.path.split('/').pop();const lastIndex = fileName.lastIndexOf('.');const extName = lastIndex > -1 ? fileName.slice(lastIndex + 1) : '';const filePath = file.path;// 1. 先从后端获取上传凭证const signatureRes = await this.getUploadSignature(extName);const signatureData = signatureRes.data.data;console.log('获取上传签名:', signatureData);// 2. 使用 uni.uploadFile 直接上传到 COSconst fileUrl = await this.uploadFileToCos(filePath, signatureData, onProgress);return fileUrl;} catch (error) {console.error('上传失败:', error);throw new Error('上传失败: ' + (error.message || '网络错误'));}}/*** 获取上传凭证* @param {string} extName - 文件扩展名* @returns {Promise<Object>} 签名数据*/getUploadSignature(extName) {return new Promise((resolve, reject) => {uni.request({url: `${this.baseUrl}/uniAppDirectSign?ext=${extName}`,method: 'POST',success: (res) => resolve(res),fail: (err) => reject(err)});});}/*** 上传文件到 COS* @param {string} filePath - 文件路径* @param {Object} signatureData - 签名数据* @param {Function} onProgress - 进度回调* @returns {Promise<string>} 文件访问 URL*/uploadFileToCos(filePath, signatureData, onProgress) {return new Promise((resolve, reject) => {uni.uploadFile({url: 'https://' + signatureData.cosHost,filePath: filePath,name: 'file',formData: {'key': signatureData.cosKey,'policy': signatureData.policy,'success_action_status': 200,'q-sign-algorithm': signatureData.qSignAlgorithm,'q-ak': signatureData.qAk,'q-key-time': signatureData.qKeyTime,'q-signature': signatureData.qSignature,'x-cos-security-token': signatureData.securityToken || ''},success: (uploadRes) => {if (uploadRes.statusCode === 200) {const fileUrl = 'https://' + signatureData.cosHost + '/' + signatureData.cosKey;resolve(fileUrl);} else {reject(new Error('上传失败'));}},fail: (err) => {reject(err);},progress: (progress) => {if (onProgress) {onProgress(progress.progress);}}});});}
}// 导出单例实例
export default new CosUploader();
3.2. 页面中使用
// 在页面中引入
import cosUploader from '@/utils/cosUploader.js'
// ....  其他代码// 上传到 COS (预签名URL方式)
async uploadToCOS(file) {try {this.uploading = true;this.uploadProgress = '开始上传...';const fileUrl = await cosUploader.uploadToCOS(file, (progress) => {this.uploadProgress = `上传进度: ${progress}%`;});this.fileUrl = fileUrl;this.uploadProgress = '上传完成';uni.showToast({ title: '上传成功', icon: 'success' });console.log('文件上传成功,访问URL:', this.fileUrl);} catch (error) {this.uploadProgress = '上传失败';console.error('上传失败:', error);uni.showToast({ title: error.message, icon: 'none' });} finally {this.uploading = false;}
},

4. 关键技术要点

4.1 动态导入的优势

// 动态导入 vs 静态导入
// 静态导入 - 编译时加载
import COS from 'cos-nodejs-sdk-v5';  // 可能编译时报错// 动态导入 - 运行时加载
async function loadCOS() {try {const cosModule = await import('cos-nodejs-sdk-v5');return cosModule.default;} catch (error) {console.error('加载失败:', error);return null;}
}

4.2 Promise 封装的必要性

使用 Promise 封装异步操作,配合 async/await 简化代码:

const signatureRes = await new Promise((resolve, reject) => {uni.request({url: '获取签名',success: resolve,fail: reject});
});

4.3 文件路径处理

// 从文件路径提取扩展名
const fileName = file.path.split('/').pop();
const lastIndex = fileName.lastIndexOf('.');
const extName = lastIndex > -1 ? fileName.slice(lastIndex + 1) : '';

4.4 上传进度监控

uni.uploadFile({progress: (progress) => {this.uploadProgress = progress.progress / 100;}
});

5. 学习收获与经验总结

5.1 动态导入的价值

  1. 错误处理:可以优雅地处理模块加载失败
  2. 按需加载:只在需要时加载模块,减少初始包体积
  3. 平台适配:根据不同平台加载不同的模块实现

5.2 开发实践

  1. Promise 封装:提升异步代码的可读性和维护性
  2. 统一错误处理:使用 try/catch 统一处理异步错误
  3. 用户体验:提供进度反馈和明确的操作结果提示
http://www.dtcms.com/a/549867.html

相关文章:

  • Spring WebSocket实战:实时通信全解析
  • 网站建设及推广方案ppt模板互联网时代的营销
  • 身份证三要素实名核验API:基于OCR与可信身份认证的技术解析
  • OCR-图片内容识别
  • wordpress 网址导航页面济南seo网站关键词排名
  • VM虚拟机共享宿主机代理(Ubuntu24.04)
  • 高端网站建设收费为何比较贵网站名称写什么
  • C++容器string
  • [人工智能-大模型-110]:用通俗易懂的语言,阐述梯度下降的本质和数学原理:就是“摸着坡度下山”——哪边最陡,就往反方向走一步,反复走,直到走到谷底。
  • 龙华做棋牌网站建设哪家好哪个平台可以免费推广
  • 网站负责人可以备案怎么看网站域名
  • AIoT项目芯片选型指南:在性能、功耗与成本的十字路口
  • Vue 3 插槽(Slot)详解
  • 刘涛做的网站网络营销的优势包括
  • Spring Boot数据访问篇:整合MyBatis操作数据库
  • 丰都网站建设报价现代示范校建设专题网站
  • Flink Keyed State 详解之七
  • 中国建设银行贵州分行网站宁波建设银行管方网站
  • NVIDIA驱动更新“翻车”?解决RTX 2060在Bilibili客户端无法加载4K视频的终极指南*
  • 贵德县建设局网站校园兼职网站开发用例图
  • JavaSE知识分享——类和对象(下)
  • 企业级K8s部署:Helm+Kustomize混合策略实现零配置漂移与10分钟多环境发布
  • 上海人才中心昆明网站词排名优化
  • jQuery Growl - 实用且易于集成的通知插件
  • Manage Provisioning Access 功能详解
  • 龙岗在线网站建设网络销售网站外包
  • NVIDIA NCCL 源码学习(十六)- nccl的ibgda(GIN)
  • 深度优先搜索(DFS)
  • 协会网站建设方案wordpress 以前文章灯箱
  • PCIe学习笔记