vue 调用pc端本地摄像头、麦克风实现拍照、录视频、录音 并上传

自己写blog只是为了下次方便使用 过程确实很烦 ,自己摸索加各大网站cv查看
可以直接使用

1、调用摄像头拍照 录屏

首先是npm i vue-video-player -D 安装依赖
在main.js里面引入

import VideoPlayer from 'vue-video-player'
require('video.js/dist/video-js.css')
require('vue-video-player/src/custom-theme.css')
Vue.use(VideoPlayer)

此处代码主要是用来覆盖video的播放按钮样式
require(’./styles/video.css’);具体内容如下

 .video-js .vjs-big-play-button {width: 72px;height: 72px;border-radius: 100%;z-index: 100;background-color: #ffffff;border: solid 1px #979797;}
<template>
<div class="main "><div class="left"><div class="vedio"><div class="shexiang"><!--图片展示--><videoref="video"width="350px"height="265px"autoplay></video><div class="btnsBackground"><el-popoverplacement="top-start"width="160"trigger="click"><div style="text-align: left; margin: 0"><div style="letter-spacing:1px">间隔 <inputv-model="everyTime"type="text"id="everyTime"style="width:30px;color:#41CFB1">秒拍一次;</div><div style="letter-spacing:1px">最长拍照时间<inputv-model="allTime"type="text"id="allTime"style="width:30px;color:#41CFB1;margin-top:10px"> 秒</div><el-checkboxv-model="checked"style="margin-top:10px">连拍模式</el-checkbox></div><el-imageslot="reference"class="photo1":src="require('@/assets/images/luxiang/setting.png')"fit="cover"></el-image></el-popover><el-imageclass="photo2"@click="recordMedia":src="require('@/assets/images/luxiang/video.png')"fit="cover"></el-image><el-image@click="takephoto"class="photo3":src="require('@/assets/images/luxiang/photo.png')"fit="fill"></el-image></div></div><span class="greySpan"> 注:1、录制视频文件时,一个视频时长不能超过一分钟;</span><spanclass="greySpan"style="margin-left:24px"> 2、点击设置按钮,可选择连拍模式</span></div></div><div class="right">//通过canvas绘画 <!--canvas截取流--><canvaswidth="350px"height="265px"ref="canvas"v-show="false"></canvas><div class="right1"><divclass="box"v-for="(item, index) in dataList":key="'photo-' + index"><divv-if="item.type=='vedio'"style="width: 100%; height:100%;border-radius: 5px;cursor: pointer;"@click="preVideo(item.src)"><video:src="item.src"style="width: 100%; height:100%;object-fit:cover"></video><div class="player"><img:src="require('@/assets/images/luxiang/player.png')"alt=""></div></div><el-imagev-if="item.type=='photo'":preview-src-list="prephotoList":src="item.src"fit="cover"style="width: 100%;height: 100%;border-radius: 5px;"></el-image><div class="del-icon"> <iclass="el-icon-delete "@click="deletePhoto(index)"></i></div></div></div>//手动分页<el-paginationsmallv-if="pagation.total>18":pager-count="5"@size-change="handleSizeChange"@current-change="handleCurrentChange"background:current-page.sync="pagation.currentPage":page-size="pagation.pagesize"layout="prev, pager, next,total":total="pagation.total"></el-pagination></div><div class="text-center margin-b10 subBtn"><el-button@click="closeContent"class="width-80 margin-r20">取 消</el-button><el-buttontype="primary"@click="submitContent"class="width-80">确 定</el-button></div>//点击视频预览<el-dialogmodalclass="videoShowDialog"destroy-on-closeclose-on-press-escapeclose-on-click-modal:visible.sync="preVideoShow"><video-playeronPlayerPlayclass="video-player vjs-custom-skin"ref="videoPlayer":playsinline="true"style="width:100%;height:100%":options="playerOptions":x5-video-player-fullscreen="true"></video-player></el-dialog></div>
</template><script>
import "video.js/dist/video-js.css";
import "vue-video-player/src/custom-theme.css";
import { videoPlayer } from "vue-video-player";
import {dataDetailApi,showFloderApi,addFolderApi,
} from "@/services/api/apply.js";
import Bus from "@/assets/js/bus";
import UploadBigFile from "./vedioUpload.vue";
import { getUserInfo, UUID } from "@/utils/helpers.js";
import { addDataFileApi } from "@/services/api/apply.js";
import { uploadFileApi, uploadMoreFileApi } from "@/services/api/common.js";
export default {components: { UploadBigFile, videoPlayer },props: ["dragShow", "name", "datasetId"],data() {return {time: null,time1: null,time2: null,preVideoShow: false,pagation: {pagesize: 18,total: 0,currentPage: 1,},checked: false,recordType: 0, //0 未录制 1 录制中mediaRecorder: null,photoAndVedioList: [],photoList: [],prephotoList: [],everyTime: "",allTime: "",list: [],remotePath: "",inputName: "",editorNameShow: false,addPathData: {},id: "",addPath: "",data: [],defaultProps: {children: "children",label: "name",},backFloader: [{}],firstPath: "",nowPath: "",lastPath: "",fileData: { listDirectory: [], listFile: [], fileServer: "" },playerOptions: {playbackRates: [0.7, 1.0, 1.5, 2.0], //播放速度autoplay: false, //如果true,浏览器准备好时开始回放。muted: false, // 默认情况下将会消除任何音频。loop: false, // 导致视频一结束就重新开始。preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)language: "zh-CN",aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。sources: [{type: "video/mp4", //这里的种类支持很多种:基本视频格式、直播、流媒体等,具体可以参看git网址项目src: "", //url地址},],poster: "", //你的封面地址// width: document.documentElement.clientWidth, //播放器宽度notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。controlBar: {timeDivider: true,durationDisplay: true,remainingTimeDisplay: false,fullscreenToggle: true, //全屏按钮},},};},methods: {//弹窗预览视频preVideo(url) {this.preVideoShow = true;this.playerOptions.sources[0].src = url;},handleSizeChange(size) {this.pagation.pagesize = size;},//切换页码handleCurrentChange(currentPage) {this.pagation.currentPage = currentPage;},//视频录制按钮控制recordMedia() {if (this.recordType == 0) {this.startMakeVideo();} else {this.stopMakeVideo();}},// 调用摄像头callCamera() {// H5调用电脑摄像头APInavigator.mediaDevices.getUserMedia({video: true,// audio: true,}).then((success) => {// 摄像头开启成功this.$refs["video"].srcObject = success;this.mediaRecorder = new MediaRecorder(success, {//因为视频和音频分开录制这里直接注释了// audioBitsPerSecond: 128000, // 音频码率videoBitsPerSecond: 1000000, // 视频码率mimeType: "video/webm;codecs=h264", // 编码格式});// 实时拍照效果this.$refs["video"].play();}).catch((error) => {console.error("摄像头开启失败,请检查摄像头是否可用!");});},//录制视频startMakeVideo() {this.mediaRecorder.start();console.log("开始采集");this.recordType = 1;this.time = window.setTimeout(() => {this.stopMakeVideo();}, 60 * 1000);},//停止录制并上传stopMakeVideo() {this.mediaRecorder.stop();// 事件let _this = this;this.mediaRecorder.ondataavailable = function (e) {console.log(e);//构建文件blob流let blob = new Blob([e.data], { type: "video/mp4" });//调用转base64函数_this.blobToBase64(blob).then((res) => {_this.photoList.push({ src: res, type: "vedio" });_this.pagation.total = _this.photoList.length;// 转化后的base64console.log("base64", res);});console.log("停止采集视频");};this.recordType = 0;window.clearTimeout(this.time);},//拍照takephoto() {if (this.checked) {if (this.everyTime && this.allTime) {//设置参数后定时拍照let time1 = window.setInterval(() => {this.savePhoto();}, this.everyTime * 1000);let time2 = setTimeout(() => {window.clearInterval(time1);}, this.allTime * 1000);} else {this.$message.error("请设置连拍参数");}} else {this.savePhoto();}},//图片展示在右侧 并保存在数组里savePhoto() {let ctx = this.$refs["canvas"].getContext("2d");// 把当前视频帧内容渲染到canvas上ctx.drawImage(this.$refs["video"], 0, 0, 350, 265);// 转base64格式、图片格式转换、图片质量压缩let imgBase64 = this.$refs["canvas"].toDataURL("image/png", 0.7); // 由字节转换为KB 判断大小console.log(this.dataURLtoFile(imgBase64));this.photoList.push({ src: imgBase64, type: "photo" });this.prephotoList.push(imgBase64);this.pagation.total = this.photoList.length;},//blob转base64blobToBase64(blob) {return new Promise((resolve, reject) => {const fileReader = new FileReader();fileReader.onload = (e) => {resolve(e.target.result);};// readAsDataURLfileReader.readAsDataURL(blob);fileReader.onerror = () => {reject(new Error("blobToBase64 error"));};});},//base64转文件dataURLtoFile(dataurl) {let arr = dataurl.split(","),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], UUID(), { type: mime });},//多文件列表上传async uploadMoreFile() {let formData = new FormData();this.photoList.forEach((it) => {formData.append("multipartFiles", this.dataURLtoFile(it.src));});const userInfo = getUserInfo();formData.append("userId", userInfo.userId);//这是项目的上传接口const res = await this.$request(uploadMoreFileApi, formData);if (res && res.status == 200 && res.data) {res.data.forEach(({ fileFixedPath, fileType, fileSize, fileName }) => {this.photoAndVedioList.push({datasetcontentPath: fileFixedPath,datasetcontentSuffix: fileType,datasetcontentSize: fileSize,datasetcontentName: fileName + "." + fileType.split("/").pop(),});});} else {this.$message.error("文件上传失败");}},closeContent() {this.$emit("close");},async getDetail() {this.treeLoading = true;const res = await this.$request(dataDetailApi, { id: this.datasetId });if (res && res.status == 200) {this.firstPath = this.nowPath = res.data.filesPath || "";this.data = [];}},deletePhoto(index) {this.photoList.splice(index + (this.pagation.currentPage - 1) * 18, 1);this.pagation.total = this.photoList.length;if (this.dataList.length == 0) {if (this.pagation.currentPage > 1)this.handleCurrentChange(this.pagation.currentPage - 1);}},async submitContent() {if (this.photoList.length == 0) {this.$message.error("请拍摄照片或数据集");return;}//调用上传先传至服务器this.uploadMoreFile().then(() => {const datasetcontentsList = this.photoAndVedioList.map((it) => {return { ...it, datasetId: this.datasetId };});//提交保存在这个项目中this.$emit("submit", datasetcontentsList);this.$emit("close");});},},created() {this.callCamera();this.getDetail();},computed: {dataList() {const dataCodeList = this.photoList;const pagesize = this.pagation.pagesize;const currentPage = this.pagation.currentPage;return dataCodeList.slice((currentPage - 1) * pagesize,pagesize * currentPage);},},
};
</script>

2、调用麦克风录音

1、创建recorder.js

// 兼容
window.URL = window.URL || window.webkitURL
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedialet HZRecorder = function (stream, config) {config = config || {}config.sampleBits = config.sampleBits || 8 // 采样数位 8, 16config.sampleRate = config.sampleRate || (44100 / 6) // 采样率(1/6 44100)let context = new (window.webkitAudioContext || window.AudioContext)()let audioInput = context.createMediaStreamSource(stream)let createScript = context.createScriptProcessor || context.createJavaScriptNodelet recorder = createScript.apply(context, [4096, 1, 1])let audioData = {size: 0, // 录音文件长度buffer: [], // 录音缓存inputSampleRate: context.sampleRate, // 输入采样率inputSampleBits: 16, // 输入采样数位 8, 16outputSampleRate: config.sampleRate, // 输出采样率oututSampleBits: config.sampleBits, // 输出采样数位 8, 16input: function (data) {this.buffer.push(new Float32Array(data))this.size += data.length},compress: function () { // 合并压缩// 合并let data = new Float32Array(this.size)let offset = 0for (let i = 0; i < this.buffer.length; i++) {data.set(this.buffer[i], offset)offset += this.buffer[i].length}// 压缩let compression = parseInt(this.inputSampleRate / this.outputSampleRate)let length = data.length / compressionlet result = new Float32Array(length)let index = 0; let j = 0while (index < length) {result[index] = data[j]j += compressionindex++}return result},encodeWAV: function () {let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate)let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)let bytes = this.compress()let dataLength = bytes.length * (sampleBits / 8)let buffer = new ArrayBuffer(44 + dataLength)let data = new DataView(buffer)let channelCount = 1// 单声道let offset = 0let writeString = function (str) {for (let i = 0; i < str.length; i++) {data.setUint8(offset + i, str.charCodeAt(i))}}// 资源交换文件标识符writeString('RIFF'); offset += 4// 下个地址开始到文件尾总字节数,即文件大小-8data.setUint32(offset, 36 + dataLength, true); offset += 4// WAV文件标志writeString('WAVE'); offset += 4// 波形格式标志writeString('fmt '); offset += 4// 过滤字节,一般为 0x10 = 16data.setUint32(offset, 16, true); offset += 4// 格式类别 (PCM形式采样数据)data.setUint16(offset, 1, true); offset += 2// 通道数data.setUint16(offset, channelCount, true); offset += 2// 采样率,每秒样本数,表示每个通道的播放速度data.setUint32(offset, sampleRate, true); offset += 4// 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4// 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2// 每样本数据位数data.setUint16(offset, sampleBits, true); offset += 2// 数据标识符writeString('data'); offset += 4// 采样数据总数,即数据总大小-44data.setUint32(offset, dataLength, true); offset += 4// 写入采样数据if (sampleBits === 8) {for (let i = 0; i < bytes.length; i++ , offset++) {let s = Math.max(-1, Math.min(1, bytes[i]))let val = s < 0 ? s * 0x8000 : s * 0x7FFFval = parseInt(255 / (65535 / (val + 32768)))data.setInt8(offset, val, true)}} else {for (let i = 0; i < bytes.length; i++ , offset += 2) {let s = Math.max(-1, Math.min(1, bytes[i]))data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)}}return new Blob([data], { type: 'audio/mp3' })}}// 开始录音this.start = function () {audioInput.connect(recorder)recorder.connect(context.destination)}// 停止this.stop = function () {recorder.disconnect()}// 获取音频文件this.getBlob = function () {this.stop()return audioData.encodeWAV()}// 回放this.play = function (audio) {let downRec = document.getElementById('downloadRec')downRec.href = window.URL.createObjectURL(this.getBlob())downRec.download = new Date().toLocaleString() + '.mp3'audio.src = window.URL.createObjectURL(this.getBlob())}// 上传this.upload = function (url, callback) {let fd = new FormData()fd.append('audioData', this.getBlob())let xhr = new XMLHttpRequest()/* eslint-disable */if (callback) {xhr.upload.addEventListener('progress', function (e) {callback('uploading', e)}, false)xhr.addEventListener('load', function (e) {callback('ok', e)}, false)xhr.addEventListener('error', function (e) {callback('error', e)}, false)xhr.addEventListener('abort', function (e) {callback('cancel', e)}, false)}/* eslint-disable */xhr.open('POST', url)xhr.send(fd)}// 音频采集recorder.onaudioprocess = function (e) {audioData.input(e.inputBuffer.getChannelData(0))// record(e.inputBuffer.getChannelData(0));}
}
// 抛出异常
HZRecorder.throwError = function (message) {alert(message)throw new function () { this.toString = function () { return message } }()
}
// 是否支持录音
HZRecorder.canRecording = (navigator.getUserMedia != null)
// 获取录音机
HZRecorder.get = function (callback, config) {if (callback) {if (navigator.getUserMedia) {navigator.getUserMedia({ audio: true } // 只启用音频, function (stream) {let rec = new HZRecorder(stream, config)callback(rec,stream)}, function (error) {switch (error.code || error.name) {case 'PERMISSION_DENIED':case 'PermissionDeniedError':HZRecorder.throwError('用户拒绝提供信息。')breakcase 'NOT_SUPPORTED_ERROR':case 'NotSupportedError':HZRecorder.throwError('浏览器不支持硬件设备。')breakcase 'MANDATORY_UNSATISFIED_ERROR':case 'MandatoryUnsatisfiedError':HZRecorder.throwError('无法发现指定的硬件设备。')breakdefault:HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name))break}})} else {HZRecorder.throwErr('当前浏览器不支持录音功能。'); return}}
}
export default HZRecorder
   上面callback的回调参数我加了stream 便于离开页面时自动关闭麦克

2、先写个播放预览动态组件
audioplay.vue

<template><divclass="audio-user"@click.stop="playThisAudio($event, url)"><div style="display:flex"><imgstyle="width:20px;height:20px"v-if="!isLive":src="require('@/assets/images/luxiang/audio.png')"//自己静态展示图alt=""><divid="run"v-if="isLive"><div></div><div></div><div></div><div></div><div></div></div></div><div>00:{{durations}}</div></div>
</template><script>
export default {name: "audioplay",components: {},props: ["url", "duration", "index"],data() {return {isLive: false,audioStatus: "播放",durations: this.duration,audio: "",};},methods: {// 播放语音playThisAudio(e, url) {this.$emit("play", this.index);let that = this;if (this.audioStatus === "播放中...") {if (this.audio) {this.audio.pause();that.isLive = false;this.audioStatus = "暂停";return false;}}if (this.audioStatus === "暂停") {if (this.audio) {this.audio.play();that.isLive = true;this.audioStatus = "播放中...";return false;}}this.audio = new Audio(url);this.audio.autoplay = true;this.audio.play();that.isLive = true;var sumDuration = this.duration;var audioI = 0;this.audio.addEventListener("timeupdate", function () {audioI++;if (audioI <= 1) {that.isLive = true;that.audioStatus = "播放中...";}let du = parseInt(sumDuration - that.audio.currentTime);if (du < 1) {that.durations = "00";} else if (du < 10) {that.durations = "0" + du;} else {that.durations = du;}});this.audio.addEventListener("ended", function () {that.durations = that.duration;that.isLive = false;that.audioStatus = "播放完成";setTimeout(() => {this.audio = "";}, 150);});},},created() {},watch: {duration(newValue) {if (this.duration) {this.durations = newValue;}},},
};
</script><style lang="less" scoped>
#run {width: 20px;height: 20px;
}
#run div {display: inline-block;position: absolute;bottom: 10px;padding: 0;margin: 0;width: 2px;height: 20px;background-color: #41cfb1;transform-origin: bottom;border-radius: 5px 5px 0 0;
}#run div:nth-child(1) {left: 5px;animation: musicWave 0.5s infinite linear both alternate;
}
#run div:nth-child(2) {left: 10px;animation: musicWave 0.2s infinite linear both alternate;
}
#run div:nth-child(3) {left: 15px;animation: musicWave 0.6s infinite linear both alternate;
}
#run div:nth-child(4) {left: 20px;animation: musicWave 0.3s infinite linear both alternate;
}
#run div:nth-child(5) {left: 25px;animation: musicWave 0.3s infinite linear both alternate;
}
@keyframes musicWave {0% {height: 2px;}100% {height: 15px;}
}
.audio-user {/* width: 3rem;height: 0.5rem; */width: 100px;height: 40px;line-height: 0.5rem;cursor: pointer;position: relative;/* background-color: #eee; *//* border-radius: 0.5rem; */display: flex;flex-direction: row;justify-content: space-around;align-items: center;/* padding: 0 0.2rem; */color: green;
}
</style>

3、Cvoice.vue

<template><div class="main "><div class="left"><div class="vedio"><div class="shexiang"><!--canvas截取流--><!--图片展示-->//这边因为要自己写动态展示波浪 能力有限就只能暴力写法 动画也是对应的<div class="big"><div :class="isLive?'bs':'nomove1'"></div><div :class="isLive?'bs':'nomove2'"></div><div :class="isLive?'bs':'nomove3'"></div><div :class="isLive?'bs':'nomove4'"></div><div :class="isLive?'bs':'nomove5'"></div><div :class="isLive?'bs':'nomove6'"></div><div :class="isLive?'bs':'nomove7'"></div><div :class="isLive?'bs':'nomove8'"></div><div :class="isLive?'bs':'nomove9'"></div><div :class="isLive?'bs':'nomove10'"></div><div :class="isLive?'bs':'nomove11'"></div><div :class="isLive?'bs':'nomove12'"></div><div :class="isLive?'bs':'nomove13'"></div><div :class="isLive?'bs':'nomove14'"></div><div :class="isLive?'bs':'nomove15'"></div><div :class="isLive?'bs':'nomove16'"></div><div :class="isLive?'bs':'nomove17'"></div><div :class="isLive?'bs':'nomove18'"></div><div :class="isLive?'bs':'nomove19'"></div><div :class="isLive?'bs':'nomove20'"></div><div :class="isLive?'bs':'nomove21'"></div><div :class="isLive?'bs':'nomove22'"></div><div :class="isLive?'bs':'nomove23'"></div><div :class="isLive?'bs':'nomove24'"></div><div :class="isLive?'bs':'nomove25'"></div><div :class="isLive?'bs':'nomove26'"></div><div :class="isLive?'bs':'nomove27'"></div><div :class="isLive?'bs':'nomove28'"></div><div :class="isLive?'bs':'nomove29'"></div><div :class="isLive?'bs':'nomove30'"></div></div><!-- <videoref="video"width="350px"height="136px"autoplay></video> --><el-buttonclass="btn"@mousedown.native="mouseStart"@mouseleave.native="mouseEnd"@mouseup.native="mouseEnd"type="primary"roundicon="el-icon-microphone">按住录制声音</el-button></div></div><div class="catagory"><span style="padding-left:5px">选择数据存储路径</span><div class="flex-r-wrap margin-t5 padding-0-5"><div class=" margin-t5 single-file"><el-tree:node-key="id":show-checkbox="false"ref="tree":data="data":props="defaultProps"@node-click="nodeClick"@node-expand="handleNodeClick"><divclass="custom-tree-node"slot-scope="{ node ,data }"><span v-if="data.type=='folder'"> <img :src='listImg("folder")' /></span><span v-else><img :src="getFileTpe(data.name)" /></span><span style="margin-left:14px">{{node.label}}</span></div></el-tree><divstyle="height:100%"@click="resetPath"></div></div><el-buttontype="primary"class="addBtn"@click="addFolder">新建文件夹</el-button></div></div></div><div class="right"><div class="right1"><divclass="box"v-for="(item, index) in dataList":key="'audio-' + index"><audioplay:url="item.src":duration="item.duration":ref='"audioPlay"+index':index="index"@play="play"></audioplay><iclass="el-icon-error text-ct3"@click="deleteAudio(index)"></i></div></div><el-paginationsmallv-if="pagation.total>30":pager-count="5"@size-change="handleSizeChange"@current-change="handleCurrentChange"background:current-page.sync="pagation.currentPage":page-size="pagation.pagesize"layout="prev, pager, next,total":total="pagation.total"></el-pagination></div><div class="text-center margin-b10 subBtn"><el-button@click="closeContent"class="width-80 margin-r20">取 消</el-button><el-buttontype="primary"@click="submitContent"class="width-80">确 定</el-button></div></div>
</template><script>
//有些接口是我自己用的可以根据自己需求改
import audioplay from "@/components/audioplay.vue";
import recording from "./recorder.js";
import {dataDetailApi,showFloderApi,addFolderApi,
} from "@/services/api/apply.js";
import Bus from "@/assets/js/bus";
import UploadBigFile from "./vedioUpload.vue";
import { getUserInfo, UUID } from "@/utils/helpers.js";
import { addDataFileApi } from "@/services/api/apply.js";
import { uploadFileApi, uploadMoreFileApi } from "@/services/api/common.js";
export default {components: { UploadBigFile, audioplay },props: ["dragShow", "name", "datasetId"],data() {return {MediaStreamTrack: null, //用来关闭媒体流的 number: "",audio: "",isLive: false,pagation: {pagesize: 30,total: 0,currentPage: 1,},audioList: [],recorder: null,interval: "",timeOut: "",audioFileList: [], // 上传语音列表startTime: "", // 语音开始时间endTime: "", // 语音结束photoAndVedioList: [],list: [],remotePath: "",inputName: "",editorNameShow: false,addPathData: {},id: "",addPath: "",data: [],defaultProps: {children: "children",label: "name",},backFloader: [{}],firstPath: "",nowPath: "",lastPath: "",fileData: { listDirectory: [], listFile: [], fileServer: "" },};},methods: {//点击空白处选择根目录 用于上传resetPath() {this.addPath = this.firstPath;this.addPathData = {};console.log(this.addPath);},//audioplay调用播放 下面对number监听  同时只能播放一个音频play(index) {this.number = index;},handleSizeChange(size) {this.pagation.pagesize = size;},//手动分页切换页码handleCurrentChange(currentPage) {this.pagation.currentPage = currentPage;},// 清除定时器clearTimer() {if (this.interval) {this.num = 60;clearInterval(this.interval);}},// 长按说话mouseStart() {this.isLive = true;this.clearTimer();this.startTime = new Date().getTime();//调用recorder.js的get() 有回调recording.get((rec, stream) => {// 当首次按下时,要获取浏览器的麦克风权限,所以这时要做一个判断处理if (rec) {this.MediaStreamTrack = stream.getTracks();// track set 流中所有 MediaStreamTrack  对象的序列 下面关闭麦克用的this.recorder = rec;this.recorder.start();}});//最长录音时间60sthis.timeOut = setTimeout(() => {this.mouseEnd();}, 60 * 1000);},// 松开时上传语音mouseEnd() {if (this.timeOut) {clearTimeout(this.timeOut);}this.clearTimer();//清定时器this.endTime = new Date().getTime();let duration = Math.ceil((this.endTime - this.startTime) / 1000 - 0.1);if (duration < 1) {this.$message.error("说话时间太短啦!");return;}if (this.recorder) {//停止录音this.recorder.stop();// 获取语音二进制文件let bold = this.recorder.getBlob();// 将获取的二进制对象转为二进制Base64文件流this.blobToBase64(bold).then((res) => {this.audioList.push({src: res,duration: duration < 10 ? "0" + duration : duration,type: "voice",});this.pagation.total = this.audioList.length;});}this.recorder = null;//录音器对象置空this.isLive = false;},//关闭麦克设备cancalCloseAudio() {this.MediaStreamTrack[0].stop();},//blob转base64blobToBase64(blob) {return new Promise((resolve, reject) => {const fileReader = new FileReader();fileReader.onload = (e) => {resolve(e.target.result);};// readAsDataURLfileReader.readAsDataURL(blob);fileReader.onerror = () => {reject(new Error("blobToBase64 error"));};});},//base64转文件dataURLtoFile(dataurl) {let arr = dataurl.split(","),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], UUID(), { type: mime });},//上传多个文件async uploadMoreFile() {let formData = new FormData();//以formData传文件this.audioList.forEach((it) => {formData.append("multipartFiles", this.dataURLtoFile(it.src));//base64转文件});const userInfo = getUserInfo();formData.append("userId", userInfo.userId);//可根据自己后台接口需求增加参数const res = await this.$request(uploadMoreFileApi, formData);if (res && res.status == 200 && res.data) {res.data.forEach(({ fileFixedPath, fileType, fileSize, fileName }) => {this.photoAndVedioList.push({datasetcontentPath: fileFixedPath,datasetcontentSuffix: fileType,datasetcontentSize: fileSize,datasetcontentName: fileName + "." + fileType.split("/").pop(),});});} else {this.$message.error("文件上传失败");}},//关闭窗口closeContent() {this.$emit("close");},//树形目录展示图标getFileTpe(name) {console.log(name);let type = name && name.split(".").pop();return this.listImg(type);},//新建文件夹async submitName() {console.log(JSON.stringify(this.addPathData) == "{}");if (JSON.stringify(this.addPathData) == "{}") {this.addPathData = {id: "folder_1",localPath: this.addPath,name: this.inputName,type: "folder",children: [{}],};}console.log(this.addPathData);const newChild = {id:this.addPathData.id +"_" +(this.addPathData.children.filter((item) => {return item.type == "folder";}).length +1),name: this.inputName,children: [],type: "folder",localPath: this.addPathData.localPath + "/" + this.inputName,};console.log(this.addPathData);const res = await this.$request(addFolderApi, {localPath: this.addPathData.localPath + "/" + this.inputName,id: this.datasetId,});if (res && res.status == 200) {if (!this.addPathData.children) {this.$set(this.addPathData, "children", []);}this.addPathData.children.push(newChild);this.editorNameShow = false;if (this.addPath == this.remotePath) {this.getDetail();}this.$emit("initData");} else if (res.status == 611) {this.$message.error("文件夹名称名称重复");}},async addFolder() {this.editorNameShow = true;},//async getDetail() {this.treeLoading = true;const res = await this.$request(dataDetailApi, { id: this.datasetId });if (res && res.status == 200) {this.firstPath = this.nowPath = res.data.filesPath || "";this.data = [];this.showList([], res.data.filesPath || "");}},//树节点点击nodeClick(data) {this.addPathData = data;this.addPath = data.localPath;console.log(this.addPathData);},//树节点展开handleNodeClick(data) {this.addPathData = data;this.addPath = data.localPath;// console.log(JSON.stringify(data.children[0]) == "{}");if (data.children && JSON.stringify(data.children[0]) !== "{}") {return;} else if (data.children && JSON.stringify(data.children[0]) == "{}") {this.showList(data, data.localPath);}},//展示树async showList(data, path) {if (data.length == 0) {this.nowPath = path;let index = path.lastIndexOf("/");if (index == 0) {this.lastPath = path.substring(0, index + 1);} else {this.lastPath = path.substring(0, index);}this.remotePath = path;this.addPath = path;const res = await this.$request(showFloderApi, {remotePath: path,});if (res && res.status == 200) {this.fileData = res.data;if (res.data.listDirectory.length != 0) {for (let i = 0; i < res.data.listDirectory.length; i++) {this.data.push({id: "folder_" + (i + 1),...res.data.listDirectory[i],children: [{}],type: "folder",});}}// for (let i = 0; i < res.data.listFile.length; i++) {//   this.data.push({//     id: "file_" + (i + 1),//     type: "file",//     ...res.data.listFile[i],//   });// }}} else {this.nowPath = path;let index = path.lastIndexOf("/");if (index == 0) {this.lastPath = path.substring(0, index + 1);} else {this.lastPath = path.substring(0, index);}const res = await this.$request(showFloderApi, {remotePath: path,});if (res && res.status == 200) {this.fileData = res.data;if (res.data.listDirectory.length == 0) {data.children.pop();} else {for (let i = 0; i < res.data.listDirectory.length; i++) {if (i == 0) {data.children.pop();}data.children.push({id: data.id + "_" + (i + 1),...res.data.listDirectory[i],children: [{}],type: "folder",});}}}}},listImg(file) {if (file === "vedio" || file === "mp4") {return require("@/assets/images/vedio.png");} else if (file === "voice" || file === "mp3") {return require("@/assets/images/voice.png");} else if (file === "ZIP") {return require("@/assets/images/zip.png");} else if (file === "txt") {return require("@/assets/images/txt.png");} else if (file === "jpg" || file === "png") {return require("@/assets/images/picture.png");} else if (file === "doc") {return require("@/assets/images/doc.png");} else if (file === "folder") {return require("@/assets/images/folder.png");} else {return require("@/assets/images/doc.png");}},// queryFile(path, file) {//   console.log(file);//   this.list.push({//     datasetcontentPath: path,//     datasetcontentSuffix: file.fileType,//     datasetcontentSize: file.size,//     datasetcontentName: file.name,//   });//   console.log(this.list);// },//删除音频deleteAudio(index) {// console.log(index);// console.log(//   index + (this.pagation.currentPage - 1) * this.pagation.pagesize// );//对应分页位置this.audioList.splice(index + (this.pagation.currentPage - 1) * this.pagation.pagesize,1);console.log(this.audioList);this.pagation.total = this.audioList.length;if (this.dataList.length == 0) {if (this.pagation.currentPage > 1)this.handleCurrentChange(this.pagation.currentPage - 1);}},//提交上传,更新内容async submitContent() {if (this.audioList.length == 0) {this.$message.error("请录制音频");return;}this.uploadMoreFile().then(() => {//上传完的回调const datasetcontentsList = this.photoAndVedioList.map((it) => {return { ...it, datasetId: this.datasetId };});this.$emit("submit", datasetcontentsList, this.addPath);//调用了更新接口this.$emit("close");});},},created() {this.getDetail();},beforeDestroy() {this.cancalCloseAudio();},watch: {//监听number 用于展示一个同时只能播放一个音频number(newValue, oldValue) {if (oldValue || oldValue === 0) {if (newValue || newValue === 0) {this.$refs["audioPlay" + oldValue][0].audio.currentTime = 0;this.$refs["audioPlay" + oldValue][0].audio.pause();this.$refs["audioPlay" + oldValue][0].audioStatus = "暂停";//控制播放时间的this.$refs["audioPlay" + oldValue][0].isLive = false;//控制动态样式}}},},computed: {dataList() {const dataCodeList = this.audioList;const pagesize = this.pagation.pagesize;const currentPage = this.pagation.currentPage;console.log(dataCodeList.slice((currentPage - 1) * pagesize, pagesize * currentPage));return dataCodeList.slice((currentPage - 1) * pagesize,pagesize * currentPage);},},
};
</script><style lang="less" scoped>
.main {display: flex;flex-wrap: nowrap;position: relative;
}.subBtn {position: absolute;top: 650px;left: 350px;z-index: 2;
}
.addBtn {padding: 3px;top: 350px;left: 15px;position: absolute;z-index: 2;
}
.single-file /deep/ .el-tree-node.is-expanded > .el-tree-node__children {display: inline;
}
.single-file {display: flex;flex-direction: column;padding-top: 10px;width: 350px;height: 300px;overflow: auto;border: 1px solid #e6e6e6;
}
#everyTime {width: 20px;border: 0px;outline: none;border-bottom: 1px solid black;
}
#allTime {outline: none;width: 30px;border: 0px;border-bottom: 1px solid black;
}
.right {margin-top: 10px;margin-left: 10px;width: 500px;height: 600px;// background: red;position: relative;display: flex;flex-direction: column;justify-content: space-between;align-items: center;overflow: auto;.right1 {align-self: flex-start;}.box {margin-top: 10px;margin-right: 10px;// padding: 10px;width: 114px;height: 46px;background: #ffffff;box-shadow: 0px 0px 10px 0px rgba(51, 51, 51, 0.06);display: inline-block;position: relative;}.text-ct3 {position: absolute;top: 0px;right: 0px;color: #ccc;font-size: 18px;cursor: pointer;}
}.left {display: inline-block;width: 350px;height: 600px;margin-left: 20px;margin-top: 20px;// background: greenyellow;.vedio {// background-color: yellowgreen;box-shadow: 0px 0px 10px 0px rgba(51, 51, 51, 0.06);.greySpan {color: grey;display: inline-block;padding-left: 10px;}.shexiang {width: 350px;height: 136px;background: #ffffff;height: 136px;position: relative;.big {width: 310px;height: 100px;margin: 0px 18px;display: flex;align-items: center;justify-content: space-around;}.bs {// display: inline-block;// position: absolute;// bottom: 76px;padding: 0;margin: 0;width: 4px;height: 28px;background: #464a49;border-radius: 2px;// transform-origin: bottom;}.bs:nth-child(1) {left: 18px;animation: musicWave 0.5s infinite linear both alternate;}.nomove1 {width: 4px;height: 10px;background: #464a49;border-radius: 2px;}.bs:nth-child(2) {left: 28px;animation: musicWave 0.2s infinite linear both alternate;}.nomove2 {width: 4px;height: 20px;background: #464a49;border-radius: 2px;}.bs:nth-child(3) {left: 38px;animation: musicWave 0.6s infinite linear both alternate;}.nomove3 {width: 4px;height: 30px;background: #464a49;border-radius: 2px;}.bs:nth-child(4) {left: 48px;animation: musicWave 0.3s infinite linear both alternate;}.nomove4 {width: 4px;height: 24px;background: #464a49;border-radius: 2px;}.bs:nth-child(5) {left: 58px;animation: musicWave 0.3s infinite linear both alternate;}.nomove5 {width: 4px;height: 26px;background: #464a49;border-radius: 2px;}.bs:nth-child(6) {left: 68px;animation: musicWave 0.5s infinite linear both alternate;}.nomove6 {width: 4px;height: 20px;background: #464a49;border-radius: 2px;}.bs:nth-child(7) {left: 78px;animation: musicWave 0.2s infinite linear both alternate;}.nomove7 {width: 4px;height: 22px;background: #464a49;border-radius: 2px;}.bs:nth-child(8) {left: 88px;animation: musicWave 0.6s infinite linear both alternate;}.nomove8 {width: 4px;height: 27px;background: #464a49;border-radius: 2px;}.bs:nth-child(9) {left: 98px;animation: musicWave 0.3s infinite linear both alternate;}.nomove9 {width: 4px;height: 30px;background: #464a49;border-radius: 2px;}.bs:nth-child(10) {left: 108px;animation: musicWave 0.3s infinite linear both alternate;}.nomove10 {width: 4px;height: 36px;background: #464a49;border-radius: 2px;}.bs:nth-child(11) {left: 118px;animation: musicWave 0.5s infinite linear both alternate;}.nomove11 {width: 4px;height: 16px;background: #464a49;border-radius: 2px;}.bs:nth-child(12) {left: 128px;animation: musicWave 0.2s infinite linear both alternate;}.nomove12 {width: 4px;height: 25px;background: #464a49;border-radius: 2px;}.bs:nth-child(13) {left: 138px;animation: musicWave 0.6s infinite linear both alternate;}.nomove13 {width: 4px;height: 19px;background: #464a49;border-radius: 2px;}.bs:nth-child(14) {left: 148px;animation: musicWave 0.3s infinite linear both alternate;}.nomove14 {width: 4px;height: 27px;background: #464a49;border-radius: 2px;}.bs:nth-child(15) {left: 158px;animation: musicWave 0.3s infinite linear both alternate;}.nomove15 {width: 4px;height: 27px;background: #464a49;border-radius: 2px;}.bs:nth-child(16) {left: 168px;animation: musicWave 0.5s infinite linear both alternate;}.nomove16 {width: 4px;height: 36px;background: #464a49;border-radius: 2px;}.bs:nth-child(17) {left: 178px;animation: musicWave 0.2s infinite linear both alternate;}.nomove17 {width: 4px;height: 28px;background: #464a49;border-radius: 2px;}.bs:nth-child(18) {left: 188px;animation: musicWave 0.6s infinite linear both alternate;}.nomove18 {width: 4px;height: 23px;background: #464a49;border-radius: 2px;}.bs:nth-child(19) {left: 198px;animation: musicWave 0.3s infinite linear both alternate;}.nomove19 {width: 4px;height: 25px;background: #464a49;border-radius: 2px;}.bs:nth-child(20) {left: 208px;animation: musicWave 0.3s infinite linear both alternate;}.nomove20 {width: 4px;height: 22px;background: #464a49;border-radius: 2px;}.bs:nth-child(21) {left: 218px;animation: musicWave 0.5s infinite linear both alternate;}.nomove21 {width: 4px;height: 19px;background: #464a49;border-radius: 2px;}.bs:nth-child(22) {left: 228px;animation: musicWave 0.2s infinite linear both alternate;}.nomove22 {width: 4px;height: 26px;background: #464a49;border-radius: 2px;}.bs:nth-child(23) {left: 238px;animation: musicWave 0.6s infinite linear both alternate;}.nomove23 {width: 4px;height: 18px;background: #464a49;border-radius: 2px;}.bs:nth-child(24) {left: 248px;animation: musicWave 0.3s infinite linear both alternate;}.nomove24 {width: 4px;height: 20px;background: #464a49;border-radius: 2px;}.bs:nth-child(25) {left: 258px;animation: musicWave 0.3s infinite linear both alternate;}.nomove25 {width: 4px;height: 25px;background: #464a49;border-radius: 2px;}.bs:nth-child(26) {left: 268px;animation: musicWave 0.5s infinite linear both alternate;}.nomove26 {width: 4px;height: 36px;background: #464a49;border-radius: 2px;}.bs:nth-child(27) {left: 278px;animation: musicWave 0.2s infinite linear both alternate;}.nomove27 {width: 4px;height: 21px;background: #464a49;border-radius: 2px;}.bs:nth-child(28) {left: 288px;animation: musicWave 0.6s infinite linear both alternate;}.nomove28 {width: 4px;height: 19px;background: #464a49;border-radius: 2px;}.bs:nth-child(29) {left: 298px;animation: musicWave 0.3s infinite linear both alternate;}.nomove29 {width: 4px;height: 25px;background: #464a49;border-radius: 2px;}.bs:nth-child(30) {left: 308px;animation: musicWave 0.3s infinite linear both alternate;}.nomove30 {width: 4px;height: 22px;background: #464a49;border-radius: 2px;}@keyframes musicWave {0% {height: 2px;}100% {height: 28px;}}.btn {cursor: pointer;position: absolute;bottom: 12px;right: 105px;z-index: 2;}}}.catagory {margin-top: 78px;position: relative;}
}
</style>

vue 调用pc端本地摄像头、麦克风实现拍照、录视频、录音 并上传到服务器指定树文件夹相关推荐

  1. 前端调用麦克风获取实时音频流和录音并上传至后台

    前端调用麦克风获取实时音频流和录音并上传至后台 index.html <!DOCTYPE html> < a href=" ">Default.html&l ...

  2. VUE调用pc端摄像头

    VUE项目调用pc端摄像头功能 (摄像头只可以用localhost启用项目访问,或者修改浏览器配置,底部有方法) 代码如下: <template> <!-- 原生摄像头-->& ...

  3. nodeJs中间件Multer详解_使用express实现本地文件/图片上传到服务器指定目录

    最终实现的效果,更改Input的值后,将图片显示出来.输入描述信息,提交后,图片上传到后台对应的路径下. Multer Multer 是一个node.js中间件,用于处理 multipart/form ...

  4. Vue 图片压缩并上传至服务器

    本文主要讲解基于 Vue + Vant ,实现移动端图片选择,并用 Canvas 压缩图片,最后上传至服务器.还会封装一个工具类,方便直接调用. 一.工具类封装 废话不多说先上代码,封装一个 Comp ...

  5. 调用PC端、手机、平板摄像头拍照

    调用PC端摄像头拍照 HTML代码: <div id="android-camera"><input type="button" title= ...

  6. vue调用电脑端摄像头实时拍照

    vue调用电脑端摄像头实时拍照 需求描述 功能实现 效果展示 需求描述 点击照相机拍照,弹出照相机拍照弹窗,点击拍照按钮,截取录像的帧,点击保存,提交数据给后台. 功能实现 1.html模块 //点击 ...

  7. Vue 调用PC摄像头拍照

    项目需求:可以本地上传头像,也可以选择拍摄头像上传. 组件: Camera组件:实现 打开.关闭摄像头.绘制.显示图片.用于上传 CameraDialog组件:使用ElementUI dialog组件 ...

  8. HTML5调用本地摄像头画面,拍照,上传服务器

    实现功能和适用业务 采集本地摄像头获取摄像头画面,拍照保存,上传服务器: 前端上传图片处理,展示,缩小,裁剪,上传服务器 实现步骤 调取本地摄像头(getUserMedia)/上传图片,将图片/视频显 ...

  9. vue适配PC端屏幕自适应

    vue适配PC端屏幕自适应 1.下载postcss-px2rem和px2rem-loader npm i postcss-px2rem px2rem-loader 2.src目录下新建utils文件夹 ...

最新文章

  1. 选课系统html页面设计,网上选课系统的设计与实现(代码)..doc
  2. 用计算机进行有理数计算时,鲁教版六上2.11《用计算器进行有理数的计算》word学案.doc...
  3. raid1 raid2 raid5 raid6 raid10的优缺点和做各自raid需要几块硬盘
  4. 如何在 SAP BTP 平台 ABAP 编程环境里消费基于 SOAP 的 Web Service
  5. php html 转xml,用PHP生成XML文档(转义字符)
  6. astrm30米分辨率高程数据下载_如何下载道路沿线1000米范围内的高程?
  7. mysql 加入系统服务_添加Mysql到Windows系统服务
  8. MFC控件——ListCtrl控件[翻译](续)
  9. CSS基本知识—浮动
  10. 谷歌返华或联手腾讯;华为否认5G专利收费;滴滴外挂让车费翻倍 | 极客头条...
  11. linux socket 开源库,linux c websocket开源库libwebsockets的编译和使用-Go语言中文社区...
  12. AndroidStudio工程打包aab文件
  13. AD7124源码 兼容AD7124-4/8 代码都经过验证 有验证的项目PCB图
  14. 一文通览支持CAN FD的Kvaser CAN/LIN总线
  15. sohutv cachecloud启动
  16. redis expire命令
  17. JAVA的下载安装教程(保姆级)
  18. TrinityCore魔兽世界服务器-注册网站
  19. abc android软件,学习ABC Learn ABC软件
  20. Codeforces Round #533(Div. 2) A.Salem and Sticks

热门文章

  1. 简单!字节跳动三面直接斩下offer,附上我的字节三面面经
  2. 学习前段HTML+CSS+JS的过程(一)
  3. 再论意识、行为和结果
  4. 屏幕分辨率、像素相关概念拾遗
  5. 解决 笔记本 键盘进水 问题
  6. jquery语法三ajax+echarts插件的使用
  7. python搭建_简单_交易系统【转载】
  8. 停招!MSRA,被曝停招国防七子及北邮学生!!
  9. 96年的小同事找了一份高薪工作
  10. Docker镜像和容器相关命令