ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

vue3版本的录制视频上传案例

2026/9/19 0:40:09 拓冰建站 浏览量
vue3版本的录制视频上传案例

HTML5 提供了一种简单而强大的方式来实现视频录制和上传功能。

通过使用 HTML5 的 video 元素和 MediaRecorder 接口,我们可以在浏览器中录制视频,并将其上传到服务器。

 

原理解释

  1. 使用 getUserMedia 方法获取用户的摄像头和麦克风权限。
  2. 创建 video 元素,将摄像头的视频流绑定到 video 元素上,实时显示摄像头的画面。
  3. 创建 MediaRecorder 对象,将 video 元素的画面录制为视频。
  4. 监听 dataavailable 事件,在录制过程中获取视频的数据块。
  5. 将数据块存储为 Blob 对象,并上传到服务器。
  6. 使用 URL.createObjectURL 方法把blob对象转化为对象url,赋值给video元素,进行预览已录制的视频。

 

注意

要创建对象 URL,可以使用 window.URL.createObjectURL() 方法,并传入 File 或 Blob 对象。如果不再需要相应数据,最好释放它占用的内容。但只要有代码在引用对象 URL,内存就不会释放。要手工释放内存,可以把对象 URL 传给 URL.revokeObjectURL()。

 

案例代码(vue3版本)

实现的主要功能:

  • 打开设备摄像头进行预览
  • 关闭摄像头
  • 开始录制视频
  • 暂停录制视频
  • 继续录制视频
  • 停止录制视频
  • 重新录制视频
  • 上传录制视频
<template><video id="preview" width="400" height="300" autoplay muted></video><video id="preview2" ref="video2" width="400" height="300" autoplay muted></video><button id="startBtn" @click="openMediaDevies">开启摄像头</button><button id="stopBtn" @click="closeMediaDevies">关闭摄像头</button><div v-if="cameraStatus === 1"><button id="startBtn" @click="startRecord">{{ recordStatus === 1 ? '继续录制' : '开始录制'}}</button><button id="startBtn" @click="pauseRecord" v-if="recordStatus === 2">暂停录制</button><button id="stopBtn" @click="stopRecord" v-if="recordStatus === 2">停止录制</button><button id="stopBtn" @click="reRecord" v-if="recordStatus === 2">重新录制</button></div></template><script setup lang="ts">
import { ref } from 'vue'// 老的浏览器可能根本没有实现 mediaDevices,所以我们可以先设置一个空的对象
if (navigator.mediaDevices === undefined) {navigator.mediaDevices = {};
}// 一些浏览器部分支持 mediaDevices。我们不能直接给对象设置 getUserMedia
// 因为这样可能会覆盖已有的属性。这里我们只会在没有 getUserMedia 属性的时候添加它。
if (navigator.mediaDevices.getUserMedia === undefined) {navigator.mediaDevices.getUserMedia = function (constraints) {// 首先,如果有 getUserMedia 的话,就获得它let getUserMedia =navigator.webkitGetUserMedia || navigator.mozGetUserMedia;// 一些浏览器根本没实现它 - 那么就返回一个 error 到 promise 的 reject 来保持一个统一的接口if (!getUserMedia) {return Promise.reject(new Error("getUserMedia is not implemented in this browser"),);}// 否则,为老的 navigator.getUserMedia 方法包裹一个 Promisereturn new Promise(function (resolve, reject) {getUserMedia.call(navigator, constraints, resolve, reject);});};
}let mediaStream;
const front = ref(true) // 前置或者后置摄像头
const recordStatus = ref(0) // 录制状态 0 停止 1 暂停 2录制
const video2 = ref(null)
const cameraStatus = ref(0) // 0 关闭 1 开启let videoEle;
let mediaRecorder;
let chunks = []; // 录制的数据
let isReRecord = false
let videoURL;// 开始录制
function startRecord() {if(!mediaRecorder) returnrecordStatus.value = 2// (闲置中,录制中或者暂停 ) (inactive, recording, or paused)mediaRecorder.state === 'paused' ? mediaRecorder.resume() : mediaRecorder.start()
}// 暂停录制
function pauseRecord() {recordStatus.value = 1mediaRecorder && mediaRecorder.pause()
}// 停止录制
function stopRecord() {recordStatus.value = 0mediaRecorder && mediaRecorder.stop()
}// 重新开始录制
function reRecord() {console.log('重新开始录制')isReRecord = truestopRecord()// mediaRecorder.requestData()
}// 初始化录制监听事件
function initMediaRecorderEvent() {// 获取录制的媒体资源 (在事件的 data 属性中会提供一个可用的 Blob 对象)mediaRecorder.ondataavailable = function(e) {// if(isReRecord) {// isReRecord = false// 	// chunks.map(i => {// 	// 	i = null// 	// })// 	chunks = JSON.parse(JSON.stringify([]))// } else {console.log('获取录制的媒体资源', chunks, e)chunks.push(e.data);// }}// 录制报错mediaRecorder.onerror = function(e) {console.log('录制报错')}// 开始录制mediaRecorder.onstart = function(e) {console.log('开始录制')}// 媒体暂停录制mediaRecorder.onpause = function(e) {console.log('媒体暂停录制')}// 暂停后恢复录制视频mediaRecorder.onresume = function(e) {console.log('暂停后恢复录制视频')}// 结束录制mediaRecorder.onstop = function(e) {// 重录标识if(isReRecord) {isReRecord = falsechunks = []startRecord()return}console.log('结束录制', chunks)if(videoURL) {// 释放Blob链接URL.revokeObjectURL(videoURL)}const blob = new Blob(chunks, { type: 'video/wasm' })videoURL = URL.createObjectURL(blob)chunks = []// videoEle.srcObject = null// videoEle.src = videoURL// videoEle.controls = truevideo2.value.src = videoURLvideo2.value.controls = true}}// 上传录制视频到服务器
function uploadBlob(blob) {const formData = new FormData()formData.append('video', blob, 'myvideo.webm')fetch('/upload', {method: 'POST',body: formData}).then(res => {console.log('视频上传成功', res)}).catch(error => {console.log('视频上传失败', error)})
}// 开启摄像头
function openMediaDevies() {// 开启摄像头,获取视频流navigator.mediaDevices.getUserMedia({ audio: true, video: { facingMode: front ? "user" : "environment" }  }).then(function (stream) {cameraStatus.value = 1mediaStream = stream// 获取video标签videoEle = document.querySelector("video");// 旧的浏览器可能没有 srcObjectif ("srcObject" in videoEle) {videoEle.srcObject = stream;} else {// 防止在新的浏览器里使用它,应为它已经不再支持了videoEle.src = window.URL.createObjectURL(stream);}videoEle.onloadedmetadata = function (e) {videoEle.play();};// 创建MediaRecorder对象mediaRecorder = new MediaRecorder(stream)initMediaRecorderEvent()}).catch(function (err) {cameraStatus.value = 0console.log(err.name + ": " + err.message);});
}// 关闭摄像头
function closeMediaDevies() {cameraStatus.value = 0if(mediaStream) {const tracks = mediaStream.getTracks();for(let i = 0; i < tracks.length; i++) {tracks[i].stop();}}}</script><style scoped>#preview {display: block;margin: 20px auto;background-color: aquamarine;}
</style>

 

参考技术

MediaDevices.getUserMedia() - Web API 接口参考 | MDN

MediaRecorder - Web API 接口参考 | MDN