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

uniapp -- 实现微信小程序、app、H5端视频上传

布局及实现代码:

<template><view class="flex flex-column p-4 grid-gap-4"><view class="flex flex-column grid-gap-4 bg-white p-4 rounded-4"><view class="font-weight-600">视频名称</view><input type="text" v-model="form.name" class="bg-placeholder rounded-4 p-4" placeholder="请输入视频名称..."maxlength="50"></view><view class="flex flex-column grid-gap-4 bg-white p-4 rounded-4"><view class="bg-placeholder rounded-4 p-4 flex flex-center" @tap="chooseVideo"><video :src="tempFilePath" class="rounded-4" style="width: 100%;height: calc(200px * 100vw / 375px);":direction="0" v-if="form.video"></video><view class="flex flex-column grid-gap-2 flex-center py-10 text-center" v-else-if="!showVideo"><view v-if="uploadState" class="flex flex-column w-100 grid-gap-2"><view class="bg-grey w-100 rounded-round" style="height: 10px;"><view class="bg-success rounded-round" style="height: 10px;transition: width .3s;":style="{ width: progress + '%' }"></view></view><text class="text-grey h6 text-center">{{ progress }}%</text></view><image src="@/static/uploads-plus.png" mode="scaleToFill" style="width: 50px;height: 50px;" v-else /><text class="text-text">上传视频</text><!-- #ifdef MP-WEIXIN --><text class="text-grey h10">或者</text><view class="flex flex-center" @tap.stop="chooseMessageFile"><text class="text-success h10">从微信中选择</text><uni-icons type="right" color="var(--xl-success)" size="12"></uni-icons></view><!-- #endif --><text class="text-grey h10">上传单人视频(建议用不说话视频)</text><text class="text-grey h10">注意每一帧都要有露脸,侧脸幅度不可过大</text></view><video :src="tempFilePath" class="rounded-4" style="width: 100%;height: calc(200px * 100vw / 375px);":direction="0" v-else-if="tempFilePath && showVideo" @loadedmetadata="loadedmetadata"></video></view><view class="font-weight-600">视频要求:</view><view class="grid-columns-4 grid-gap-4"><view class="grid-column-2 flex"><text class="text-grey h10">视频方向:</text><text class="text-text h10">横向或纵向</text></view><view class="grid-column-2 flex"><text class="text-grey h10">分辨率:</text><text class="text-text h10">360p~4K</text></view><view class="grid-column-2 flex"><text class="text-grey h10">文件格式:</text><text class="text-text h10">mp4,mov</text></view><view class="grid-column-2 flex"><text class="text-grey h10">文件大小:</text><text class="text-text h10">不超过300M</text></view><view class="grid-column-2 flex"><text class="text-grey h10">视频时长:</text><text class="text-text h10">10秒~5分钟</text></view></view></view></view>
</template>
<script lang="ts" setup>
import { ref, nextTick } from 'vue';
import { $message, $http, $page } from "@/utils";
import { onLoad, onShow } from "@dcloudio/uni-app";const form = ref({name: '',video: ''
});
const tempFilePath = ref('');
let fileObj = {name: '',type: '',size: ''
}const loadedmetadata = (e: any) => {showVideo.value = false;console.log('选择的文件:', e);if (e.detail?.duration < 5) {uni.showModal({title: '提示',content: '视频时长不得少于5秒',showCancel: false,confirmText: '我知道了'});form.value.video = '';tempFilePath.value = '';} else if (!form.value.video) {uploadFile();}
}
const submit = () => {if (!form.value.name) {$message.error('请输入数字人分身名称');return;}console.log('form:', form)if (!form.value.video) {$message.error('请上传视频');return;console.log('数据:', form.value);}
}
const progress = ref(0);
const uploadState = ref(false);
const showVideo = ref(false);
// 其他端选择视频
const chooseVideo = () => {uni.chooseVideo({compressed: true, // WEBCONFIG.value?.upload_video_compressed ? true : false,camera: 'front',success: (res: any) => {console.log('success:', res)// 保存文件信息fileObj = res.tempFiletempFilePath.value = res.tempFilePath;showVideo.value = true;form.value.video = '';// APP不支持@loadedmetadata// #ifdef APP-PLUSif (showVideo.value && tempFilePath.value) {const val = {detail: {duration: res.duration}}loadedmetadata(val)}// #endif// form.value.video = res.tempFilePath;}})
}// 文件上传
const uploadFile = () => {uni.showLoading({title: '上传中...',mask: true})progress.value = 0;uploadState.value = true;// 微信小程序上传视频const task = $http.upload('Generation/uploadVideo', {filePath: tempFilePath.value,name: 'file',success: (res: any) => {uni.hideLoading();uploadState.value = false;if (res.statusCode === 200) {try {const data = JSON.parse(res.data);if (data.code === $http.ResponseCode.SUCCESS) {form.value.video = data.data.url;nextTick(() => {showVideo.value = true;})} else {$message.error(data.msg);}return;} catch (e) {}}$message.error('上传失败:' + JSON.stringify(res));},fail: (err: any) => {uni.hideLoading();uploadState.value = false;$message.error('上传失败:' + JSON.stringify(err));},}, true);task.onProgressUpdate((res: any) => {progress.value = res.progress;console.log('上传进度' + res.progress);console.log('已经上传的数据长度' + res.totalBytesSent);console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);});
}// 微信小程序选择视频
const chooseMessageFile = () => {wx.chooseMessageFile({count: 1,type: 'file',extension: ['mp4', 'mov'],success: (res: any) => {tempFilePath.value = res.tempFiles[0].path;showVideo.value = true;form.value.video = '';}})
}onShow(() => {
})</script>
<style lang="scss" scoped>
.generation-example-item {position: relative;--w: 34.6666666667vw;--h: 22.6666666667vw;height: var(--h);width: var(--w);flex-shrink: 0;.videocam {position: absolute;top: 50%;right: 50%;transform: translate(50%, -50%);.play-icon-image {width: 30px;height: 30px;}}
}.footer-placeholder {--footer-height: 115px;height: var(--footer-height);box-sizing: content-box;.footer {background: radial-gradient(60% 80% at 50% 100%,rgba(79, 255, 249, 0.2) 0%,rgba(181, 226, 255, 0) 80%,rgba(255, 238, 240, 0) 100%),radial-gradient(20% 90% at 50% 90%,rgba(141, 255, 164, 0.3) 0%,rgba(255, 255, 255, 0) 100%), var(--xl-bg);height: var(--footer-height);box-sizing: content-box;position: fixed;bottom: 0;left: 0;right: 0;z-index: 100;}
}
</style>

实现页面效果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件太大时上传速度很慢。
优化:实现TOS 预签名URL上传
H5和APP端可使用

const uploadFile = () => {uni.showLoading({title: '上传中...',mask: true})progress.value = 0;uploadState.value = true;console.log('是否是微信小程序:', isWeixin.value)if (isWeixin.value) {// 微信小程序上传视频const task = $http.upload('Generation/uploadVideo', {filePath: tempFilePath.value,name: 'file',success: (res: any) => {uni.hideLoading();uploadState.value = false;if (res.statusCode === 200) {try {const data = JSON.parse(res.data);if (data.code === $http.ResponseCode.SUCCESS) {form.value.video = data.data.url;nextTick(() => {showVideo.value = true;})} else {$message.error(data.msg);}return;} catch (e) {}}$message.error('上传失败:' + JSON.stringify(res));},fail: (err: any) => {uni.hideLoading();uploadState.value = false;$message.error('上传失败:' + JSON.stringify(err));},}, true);task.onProgressUpdate((res: any) => {progress.value = res.progress;console.log('上传进度' + res.progress);console.log('已经上传的数据长度' + res.totalBytesSent);console.log('预期需要上传的数据总长度' + res.totalBytesExpectedToSend);});} else {// H5和APP视频上传// 获取预签名URL:uploadTosconsole.log('tempFilePath', tempFilePath.value)let fileName = fileObj.name;let fileType = fileObj.type;let fileSize = fileObj.size;$http.post('Generation/uploadTos', {data: JSON.stringify({type: fileType,name: fileName,size: fileSize,})}).then((res: any) => {// console.log('获取预签名URL:', res);if (res.code == 200) {let url = res.data;axios({url: url,method: "put",data: fileObj,contentType: false,processData: false,//原生获取上传进度的事件onUploadProgress: (progressEvent: any) => {let processTwo = ((progressEvent.loaded / progressEvent.total) * 100) | 0;progress.value = processTwo * 1;let progressTex = `上传进度:${processTwo}%`;console.log(progressTex);},}).then((res2: any) => {if (res2.status !== 200) {uni.hideLoading();uploadState.value = false;$message.error('上传失败');return}// 截取视频地址:// let responseURL = res2.request.responseURL.split('?')[0]let responseURL = res2.request.responseURL.split('?')[0]// 视频上传完成回调后端接口---判断上传成功or失败$http.post('Generation/uploadAiBack', {data: { url: responseURL }}).then((res3: any) => {// console.log('视频上传完成回调', res3);// 视频上传成功if (res3.code === $http.ResponseCode.SUCCESS) {form.value.video = url;uni.hideLoading();uploadState.value = false;nextTick(() => {showVideo.value = true;})} else {// 视频上传失败uni.hideLoading();uploadState.value = false;$message.error(res3.msg);}})})} else {uni.hideLoading();uploadState.value = false;$message.error('获取预签名URL失败');}})}
}

http请求封装:这是开发的项目里的文件,非自己封装的,可用自己的请求方法更改

import { useUserStore } from "@/stores";
import { useStorage } from "./storage";
let baseHOST = ''; // 请求地址let baseURL = `${baseHOST}api/`;
// #ifdef H5
import system from './h5/system';
export const $system = system;
// #endif
// #ifdef MP-WEIXIN
import system from './mp-weixin/system';
export const $system = system;
// #endif
// #ifdef MP-TOUTIAO
import system from './mp-toutiao/system';
export const $system = system;
// #endif
// #ifdef APP
import system from './app/system';
export const $system = system;
// #endif
if (system.isProd()) {baseHOST = `${system.host()}/app/ycDigitalHuman/`;baseURL = `${system.host()}/app/ycDigitalHuman/api/`;
}
const RequestHeaders = (options?: any) => {const header: any = {};const { getToken, hasLogin } = useUserStore();if (hasLogin()) {header['Authorization'] = getToken();}if (options && options.header) {for (let x in options.header) {header[x] = options.header[x]}}const storeage = useStorage();const appid = system.appid();if (appid) {header['Appid'] = appid;const icode = storeage.get('ICODE.' + appid);if (icode) {header['Icode'] = icode;}if (storeage.get('icode')) {header['Icode'] = storeage.get('icode');}const PUID = storeage.get('PUID.' + appid);if (PUID) {header['Puid'] = PUID;}}return header;
}
export const $http = {ResponseCode: {SUCCESS: 200,NEED_LOGIN: 12000,PAY_SUCCESS: 9000,PAY_NOTPAY: 11000,NEED_PAY: 60000,CLOCK_IN: 70000,},baseURL,baseHOST,RequestHeaders,image: (path: string) => {return `${baseHOST}${path}`;},get: (url: string, options?: any): Promise<any> => {return new Promise((resolve, reject) => {uni.request({...options,timeout: 6000000,header: RequestHeaders(options),url: baseURL + url,success: (res: any) => {if (res.statusCode === 200) {resolve(res.data);} else {reject(res);}},fail: (err: any) => {reject(err);},complete: () => { }})})},post: (url: string, options: any): Promise<any> => {return new Promise((resolve, reject) => {uni.request({...options,timeout: 6000000,header: RequestHeaders(options),url: baseURL + url,method: 'POST',success: (res: any) => {if (res.statusCode === 200) {resolve(res.data);} else {reject(res);}},fail: (err: any) => {reject(err);},complete: () => { }})})},upload: (url: string, options: any, task?: false): Promise<any> | UniNamespace.UploadTask => {if (task) {return uni.uploadFile({...options,timeout: 6000000,header: RequestHeaders(options),url: baseURL + url})} else {return new Promise((resolve, reject) => {uni.uploadFile({...options,timeout: 6000000,header: RequestHeaders(options),url: baseURL + url,success: (res: any) => {if (res.statusCode === 200) {try {const data = JSON.parse(res.data);resolve(data);} catch (e) {reject(res);}} else {reject(res);}},fail: (err: any) => {reject(err);},})})}}
}

相关文章:

  • 每日英语:每周背10句
  • `rfind()` 从字符串的右侧开始查找指定子字符串首次出现的位置
  • 达梦并行收集统计信息
  • CLIP和SimCLR集成到图像-文本检索系统技术实现步骤和部署方案(代码版)
  • 《仙剑奇侠传二》游戏秘籍
  • Linux常见指令介绍下(入门级)
  • Vue3集成浏览器API实时语音识别
  • Ubuntu K8s集群安全加固方案
  • 如何下载适用于语音识别功能增强的Google Chrome浏览器
  • 2025 年“泰迪杯”数据挖掘挑战赛B题——基于穿戴装备的身体活动监测问题分析
  • 堆和二叉树--数据结构初阶(3)(C/C++)
  • 本地缓存大杀器-Caffeine
  • Obsidian和Ollama大语言模型的交互过程
  • 基于AI与drawio的图表生成技术及其在学术研究中的应用前景分析
  • 基于 EFISH-SBC-RK3588 的无人机智能巡检终端方案‌
  • attention-transformer-test
  • Anything V4/V5 模型汇总
  • 60个GitLab CI/CD 面试问题和答案
  • React 与 Vue:两大前端框架的深度对比
  • 使用MCP Python SDK构建面向大语言模型的上下文协议服务
  • A股三大股指收跌:地产股领跌,银行股再度走强
  • 第1现场|无军用物资!伊朗港口爆炸已遇难40人伤1200人
  • 幸福航空取消“五一”前航班,财务人员透露“没钱飞了”
  • 央行副行长谈美债和美元波动:单一市场、单一资产的变动,对外储影响总体有限
  • 李在明当选韩国共同民主党总统候选人
  • 地下管道密布成难题,道路修整如何破局?