微信小程序上传腾讯云cos封装
下载js工具包
https://github.com/tencentyun/cos-wx-sdk-v5/blob/master/demo/lib/cos-wx-sdk-v5.js
把上面的js文件下载下来
封装上传的工具包cor.js
js
// videoUploadService.js
import COS from "./cos-wx-sdk-v5";
/**
* 腾讯云COS视频上传服务
*/
class VideoUploadService {
constructor(config = {}) {
// 保存配置
this.config = {
// 必填参数
bucket: config.bucket || '', // 存储桶名称
region: config.region || 'ap-beijing', // 地区
// 权限参数(应该从后端获取)
TmpSecretId: config.TmpSecretId || '',
TmpSecretKey: config.TmpSecretKey || '',
SecurityToken: config.SecurityToken || '',
// 可选参数
videoPrefix: config.videoPrefix || 'wxapp/videos/',
parallel: config.parallel || 2, // 并发数
timeout: config.timeout || 300000, // 超时时间(毫秒)
chunkSize: 1024 * 1024 * 20, // 设置较大的分片大小,基本不会触发分片
};
this.cos = null;
this.currentUploadTask = null;
this.isUploading = false;
// 初始化COS实例
this.initCOS();
}
/**
* 初始化COS
*/
initCOS() {
this.cos = new COS({
SecretId: this.config.TmpSecretId,
SecretKey: this.config.TmpSecretKey,
SecurityToken: this.config.SecurityToken,
ChunkSize: this.config.chunkSize, // 设置为20MB,10秒视频基本不会超过
Parallel: this.config.parallel,
Timeout: this.config.timeout
});
}
/**
* 上传单个视频
*/
uploadVideo(filePath, onProgress = null, customKey = null) {
if (!this.cos) {
return Promise.reject(new Error('COS未初始化'));
}
if (this.isUploading) {
return Promise.reject(new Error('当前有视频正在上传,请等待或取消'));
}
this.isUploading = true;
return new Promise((resolve, reject) => {
const fileName = filePath.split('/').pop();
const fileExt = this.getVideoExt(fileName);
const key = customKey || this.generateVideoKey(fileExt);
console.log('开始上传视频:', {
filePath,
fileName,
key,
bucket: this.config.bucket,
region: this.config.region
});
this.currentUploadTask = this.cos.uploadFile({
Bucket: this.config.bucket,
Region: this.config.region,
Key: key,
FilePath: filePath,
onProgress: (progressData) => {
if (onProgress) {
const percent = Math.round(progressData.percent * 100);
const speed = progressData.speed ? `${(progressData.speed / 1024).toFixed(2)}KB/s` : '0KB/s';
onProgress({
percent,
speed,
loaded: progressData.loaded,
total: progressData.total
});
}
},
onFileFinish: (err, data) => {
this.isUploading = false;
this.currentUploadTask = null;
if (err) {
console.error('视频上传失败:', err);
reject(new Error(`上传失败: ${err.message || '未知错误'}`));
return;
}
console.log('上传完成:', data);
if (data.statusCode === 200) {
const result = {
success: true,
url: data.Location,
key: data.Key,
videoUrl: `https://${data.Location}`,
etag: data.ETag,
headers: data.headers
};
resolve(result);
} else {
reject(new Error(`服务器响应异常: HTTP ${data.statusCode}`));
}
}
});
});
}
/**
* 取消上传
*/
cancelUpload() {
if (this.currentUploadTask) {
this.currentUploadTask.cancel();
this.currentUploadTask = null;
this.isUploading = false;
return true;
}
return false;
}
/**
* 生成视频存储key
*/
generateVideoKey(ext = 'mp4') {
const timestamp = Date.now();
const randomStr = Math.random().toString(36).substr(2, 8);
return `${this.config.videoPrefix}${timestamp}_${randomStr}.${ext}`;
}
/**
* 获取视频扩展名
*/
getVideoExt(filename) {
const ext = filename.toLowerCase().split('.').pop();
const videoExts = ['mp4', 'mov', 'avi', 'm4v', 'webm', 'flv', '3gp', 'wmv'];
return videoExts.includes(ext) ? ext : 'mp4';
}
/**
* 更新配置
*/
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.initCOS(); // 重新初始化COS实例
}
}
// 不再使用单例模式,因为每个上传任务可能需要不同的配置
export const createVideoUploader = (config) => {
return new VideoUploadService(config);
};使用工具,做个选择视频并上传的示例代码
wxml代码为
js
<!-- pages/uploadVideo/uploadVideo.wxml -->
<view class="container">
<!-- 选择视频区域 -->
<view class="select-area" wx:if="{{!videoPath && !uploading}}">
<view class="select-btn" bindtap="selectVideo">
选择视频
</view>
</view>
<!-- 视频信息 -->
<view class="video-info" wx:if="{{videoPath && !uploading && !result}}">
<view>视频: {{videoName}}</view>
<view>大小: {{videoSize}}</view>
<view class="upload-btn" bindtap="startUpload">
开始上传
</view>
</view>
<!-- 上传中 -->
<view class="uploading" wx:if="{{uploading}}">
<view>上传中... {{progress}}%</view>
<progress percent="{{progress}}" show-info />
</view>
<!-- 上传结果 -->
<view class="result" wx:if="{{result}}">
<view class="result-title" wx:if="{{result.success}}">
✓ 上传成功
</view>
<view class="result-title error" wx:else>
✗ 上传失败
</view>
<view wx:if="{{result.success && result.url}}">
<view class="url-text">{{result.url}}</view>
<view class="action-btn" bindtap="copyUrl">复制链接</view>
</view>
<view wx:else-if="{{!result.success}}">
<view class="error-text">{{result.error}}</view>
</view>
<view class="reupload-btn" bindtap="reUpload">
重新上传
</view>
</view>
</view>wxss代码为
js
/* pages/uploadVideo/uploadVideo.wxss */
.container {
padding: 40rpx;
min-height: 100vh;
background: #f5f5f5;
}
.select-area {
padding: 100rpx 0;
text-align: center;
}
.select-btn {
display: inline-block;
padding: 20rpx 60rpx;
background: #07c160;
color: white;
border-radius: 10rpx;
font-size: 32rpx;
}
.video-info {
background: white;
padding: 40rpx;
border-radius: 10rpx;
margin-bottom: 30rpx;
}
.upload-btn {
margin-top: 30rpx;
padding: 20rpx;
background: #1989fa;
color: white;
text-align: center;
border-radius: 10rpx;
}
.uploading {
background: white;
padding: 40rpx;
border-radius: 10rpx;
text-align: center;
}
.result {
background: white;
padding: 40rpx;
border-radius: 10rpx;
}
.result-title {
font-size: 36rpx;
font-weight: bold;
color: #07c160;
margin-bottom: 30rpx;
}
.result-title.error {
color: #f44;
}
.url-text {
background: #f5f5f5;
padding: 20rpx;
border-radius: 5rpx;
font-size: 24rpx;
word-break: break-all;
margin-bottom: 20rpx;
}
.action-btn {
padding: 20rpx;
background: #1989fa;
color: white;
text-align: center;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.error-text {
color: #f44;
margin-bottom: 20rpx;
}
.reupload-btn {
padding: 20rpx;
background: #ff976a;
color: white;
text-align: center;
border-radius: 10rpx;
}js代码为
js
// pages/uploadVideo/uploadVideo.js
import { createVideoUploader } from '../../../../utils/cos';
Page({
data: {
// 上传状态
uploading: false,
progress: 0,
// 视频信息
videoPath: '',
videoName: '',
videoSize: '',
// 上传结果
result: null
},
/**
* 选择视频
*/
selectVideo() {
wx.chooseMedia({
count: 1,
mediaType: ['video'],
sourceType: ['album', 'camera'],
success: (res) => {
console.log('res',res)
const video = res.tempFiles[0];
console.log('video',video)
const sizeMB = (video.size / 1024 / 1024).toFixed(2);
this.setData({
videoPath: video.tempFilePath,
videoName: video.tempFilePath.split('/').pop(),
videoSize: sizeMB + 'MB',
result: null
});
},
fail: () => {
wx.showToast({
title: '选择失败',
icon: 'error'
});
}
});
},
// 在页面JS中
async startUpload() {
if (!this.data.videoPath) {
wx.showToast({
title: '请先选择视频',
icon: 'none'
});
return;
}
if (this.data.uploading) return;
// 1. 先获取凭证
// const credentials = await this.getCredentials(); // 这里正常是从接口取,我目前在下面写死了临时密钥和Token
const credentials = {
"Return": 1,
"Result": "{\"Credentials\":{\"Token\":\"XXXXXX\",\"TmpSecretId\":\"XXXXX\",\"TmpSecretKey\":\"XXXXXX\"},\"RequestId\":\"XXXXXX\",\"Expiration\":\"XXXXXXX\",\"ExpiredTime\":1772701464,\"StartTime\":1772615064,\"CDNPath\":\"\"}",
"ReturnInfo": ""
}
if (!credentials) return;
// 2. 解析凭证(你的凭证格式是JSON字符串嵌套在Result中)
let cosCredentials;
try {
const resultData = JSON.parse(credentials.Result);
cosCredentials = resultData.Credentials;
} catch (err) {
wx.showToast({
title: '凭证解析失败',
icon: 'error'
});
console.error('凭证解析失败:', err, credentials);
return;
}
// 3. 创建上传器 - 使用解析后的凭证(这里就是主要的上传逻辑,将凭证上传给函数就上传了)
const uploader = createVideoUploader({
bucket: 'XXXX', // 你的bucket
region: 'XXXXXX', // 你的region
TmpSecretId: cosCredentials.TmpSecretId,
TmpSecretKey: cosCredentials.TmpSecretKey,
SecurityToken: cosCredentials.Token
});
this.setData({ uploading: true, progress: 0 });
try {
// 这里上传微信小程序生成的临时文件路径即可
const result = await uploader.uploadVideo(this.data.videoPath, (p) => {
this.setData({ progress: Math.round(p.percent) });
});
this.setData({
result: {
success: true,
url: result.videoUrl
},
uploading: false
});
wx.showToast({
title: '上传成功',
icon: 'success'
});
} catch (error) {
this.setData({
result: {
success: false,
error: error.message
},
uploading: false
});
wx.showToast({
title: '上传失败: ' + error.message,
icon: 'error'
});
}
},
/**
* 获取COS凭证
*/
getCredentials() {
return new Promise((resolve) => {
// 这里替换为你的后端接口
wx.request({
url: 'https://your-backend.com/api/get-cos-credentials',
method: 'GET',
success: (res) => {
if (res.data.code === 0) {
resolve(res.data.data);
} else {
wx.showToast({
title: '获取凭证失败',
icon: 'error'
});
resolve(null);
}
},
fail: () => {
wx.showToast({
title: '网络错误',
icon: 'error'
});
resolve(null);
}
});
});
},
/**
* 复制链接
*/
copyUrl() {
if (this.data.result?.url) {
wx.setClipboardData({
data: this.data.result.url,
success: () => {
wx.showToast({
title: '已复制',
icon: 'success'
});
}
});
}
},
/**
* 重新上传
*/
reUpload() {
this.setData({
result: null,
videoPath: '',
videoName: '',
videoSize: ''
});
}
});