VUE调用摄像头,拍摄视频上传demo

前端代码

<template><div id="videoDemo"><div><el-form ref="uploadForm" :model="uploadForm" label-width="120px"><el-row><el-form-item label="单号编码" prop="code"><el-input v-model="uploadForm.code" placeholder="单号" @keyup.enter.native="checkOrder" /></el-form-item></el-row><el-row><button @click="startRecording" class="large-button" :disabled="isRecording">开始录制</button><button @click="stopRecording" class="large-button" :disabled="!isRecording">停止录制</button></el-row></el-form></div><div style="display: flex;align-items: stretch; "><video id="video" ref="video" width="1000" height="750" autoplay></video></div></div>
</template><script>
import axios from 'axios';export default {data() {return {mediaRecorder: null,isRecording: false,stream: null,recordedBlobs: [],uploadInterval: null,uploadForm: {code: undefined,lastCode: undefined,},isUploading: false, // 新增一个标志,用于判断是否正在上传};},methods: {checkOrder() {console.log('我进来了checkOrder');// 当我文本框回车的时候,校验单号是否和上一次单号一致,不一致的话,就停止录制并上传,继续开启下一次的录制if (this.uploadForm.code != undefined && this.uploadForm.code.trim().length > 0) {if (this.uploadForm.lastCode == undefined) {this.startRecording();} else {if (this.uploadForm.code != this.uploadForm.lastCode) {this.stopRecording(); // 在这里调用上传视频的方法,确保上传的是完整的视频文件setTimeout(() => {this.startRecording();}, 500); // 延迟500毫秒后再开始新的录制,确保状态更新}}console.log(this.uploadForm.lastCode);this.uploadForm.lastCode = this.uploadForm.code;}},handleMediaRecorderStop() {// 最后一次上传时调用// 确保不在上传过程中才进行上传if (!this.isUploading) {this.isUploading = true; // 标记为正在上传this.uploadVideo();}},startRecording() {console.log('我进来了startRecording');if (!this.stream) {this.askForPermission();return;}this.setupMediaRecorder();this.isRecording = true;this.setupUploadInterval();console.log('start recording 执行结束');},setupMediaRecorder() {this.recordedBlobs = [];if (this.mediaRecorder && this.mediaRecorder.state === "recording") {// 如果当前已经在录制中,则不再尝试开始新的录制return;}this.mediaRecorder = new MediaRecorder(this.stream, { mimeType: 'video/webm' });this.mediaRecorder.ondataavailable = event => {if (event.data && event.data.size > 0) {this.recordedBlobs.push(event.data);}};// this.mediaRecorder.onstop = this.handleMediaRecorderStop;this.mediaRecorder.onstop = () => {this.handleMediaRecorderStop();if (this.isRecording) {// 如果仍在录制状态,稍后尝试开始新的录制段setTimeout(() => {this.setupMediaRecorder();if (this.mediaRecorder.state === "inactive") {this.mediaRecorder.start(10);// 每秒生成一个chunk}}, 100); // 延迟100毫秒后尝试开始新的录制,确保状态更新}};if (this.mediaRecorder.state === "inactive") {this.mediaRecorder.start(10); // 开始新的录制}},stopRecording() {console.log('我进来了stop');if (this.mediaRecorder && this.mediaRecorder.state === "recording") {this.mediaRecorder.stop();this.isRecording = false;clearInterval(this.uploadInterval);this.uploadInterval = null;// if (!this.isUploading) { // 检查是否已经在上传//     this.isUploading = true; // 设置正在上传标志//     setTimeout(() => {//         this.uploadVideo(); // 在延迟后上传视频//     }, 200); // 延迟200毫秒// }}// setTimeout(async () => { // 添加延迟以确保视频文件完整生成// }, 1000); // 延迟1秒console.log('stop执行结束');},getFileName() {const timestamp = new Date();let filename = '';const dd = `${timestamp.getFullYear()}-${timestamp.getMonth() + 1}-${timestamp.getDate()}-${timestamp.getHours()}-${timestamp.getMinutes()}-${timestamp.getSeconds()}`;if (this.uploadForm.code != undefined && this.uploadForm.code.trim().length > 0) {filename = this.uploadForm.code + '_' + dd + '.webm';} else {filename = dd + '.webm';}return filename;},uploadVideo() {const blob = new Blob(this.recordedBlobs, { type: 'video/webm' });const formData = new FormData();let filename = this.getFileName();formData.append('file', blob, filename);axios.post('http://localhost:8421/upload_one', formData).then(response => {console.log('Upload success:', response);}).catch(error => {console.error('Upload error:', error);}).finally(() => {this.isUploading = false; // 上传完成后,无论成功还是失败,都重置上传状态});;},askForPermission() {navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {this.stream = stream;this.$refs.video.srcObject = stream;// 只有在成功获取流后才能开始录制// this.startRecording();}).catch(error => {console.error('Media access error:', error);});},setupUploadInterval() {clearInterval(this.uploadInterval); // 清除之前的定时器this.uploadInterval = setInterval(() => {if (this.mediaRecorder && this.mediaRecorder.state === "recording" ) {this.mediaRecorder.stop(); // 停止当前录制,触发onstop事件上传当前录制的视频}}, 30000); // 每隔30秒上传一次},},mounted() {this.askForPermission();},
};
</script>
<style>
.large-button {padding: 25px 50px;/* 调整内边距来增加按钮的大小 */font-size: 40px;/* 调整字体大小 */
}
</style>

后端代码使用python接收上传文件

# -*- ecoding: utf-8 -*-
# @ModuleName: test002
# 当你要使用这份文件时,
# 代表你已经完全理解文件内容的含义,
# 并愿意为使用此文件产生的一切后果,付全部责任
# @Funcation: 
# @Author: darling
# @Time: 2024-06-17 14:21
from flask_cors import CORS
from flask import Flask, request
import os
from loguru import loggerapp = Flask(__name__)
CORS(app)@app.route('/upload_one', methods=['POST'])
def upload_one():'''前端上传,批量选择后,前端循环上传,后端单个接收:return:'''file = request.files['file']  # 获取上传的文件if file:logger.info('获取到文件{}', file.filename)file.save(os.path.join('files', file.filename))  # 保存文件到当前目录logger.info('保存结束{}', file.filename)return '文件上传成功!'else:return '文件上传失败!'@app.route('/upload_batch', methods=['POST'])
def upload_batch():'''前端上传,批量选择后一次性上传,后端循环保存:return:'''files = request.files.getlist('files')  # 获取上传的文件列表if files:for file in files:logger.info('获取到文件{}', file.filename)file.save(os.path.join('files', file.filename))  # 保存文件到当前目录logger.info('保存结束{}', file.filename)return '文件上传成功!'else:return '文件上传失败!'if __name__ == '__main__':app.run(host='0.0.0.0', port=8421)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/866870.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

重载赋值运算符

c编译器可能会给类添加四个函数 1默认构造函数 2默认析构函数 3默认拷贝构造函数&#xff0c;对成员变量进行浅拷贝。 4默认赋值函数&#xff0c;队成员变量进行浅拷贝。 #include<iostream> using namespace std; class CGirl { public:int m_bh;string m_name;voi…

【VUE基础】VUE3第三节—核心语法之computed、watch、watcheffect

computed 接受一个 getter 函数&#xff0c;返回一个只读的响应式 ref 对象。该 ref 通过 .value 暴露 getter 函数的返回值。它也可以接受一个带有 get 和 set 函数的对象来创建一个可写的 ref 对象。 创建一个只读的计算属性 ref&#xff1a; <template><div cl…

3033.力扣每日一题7/5 Java

博客主页&#xff1a;音符犹如代码系列专栏&#xff1a;算法练习关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 目录 思路 解题方法 时间复杂度 空间复杂度 Code 思路 首先创建一个与…

【C++】unordered系列容器的封装

你很自由 充满了无限可能 这是很棒的事 我衷心祈祷你可以相信自己 无悔地燃烧自己的人生 -- 东野圭吾 《解忧杂货店》 unordered系列的封装 1 unordered_map 和 unordered_set2 改造哈希桶2.1 模版参数2.2 加入迭代器 3 上层封装3.1 unordered_set3.2 unordered_map 4 面…

基于springboot的工作绩效管理系统的设计与实现+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

零基础学习MySQL---库的相关操作

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、创建数据库 1.语法 CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] .…

Android 简单快速实现 下弧形刻度尺(滑动事件)

效果图&#xff1a; 直接上代码&#xff1a; package com.my.view;import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Pai…

WordPress付费进群V2主题,多种引流方法,引私域二次变现

全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也是如此随机刷新不重复 进群页面简介&#xff0c;群聊名称&#xff0c;群内展示&…

UML2.0-系统架构师(二十四)

1、&#xff08;重点&#xff09;系统&#xff08;&#xff09;在规定时间内和规定条件下能有效实现规定功能的能力。它不仅取决于规定的使用条件等因素&#xff0c;还与设计技术有关。 A可靠性 B可用性 C可测试性 D可理解性 解析&#xff1a; 可靠性&#xff1a;规定时间…

ServiceImpl中的参数封装为Map到Mapper.java中查询

ServiceImpl中的参数封装为Map到Mapper.java中查询&#xff0c;可以直接从map中获取到key对应的value

论文阅读【时间序列】DSformer

论文阅读【时间序列】DSformer arxive: DSformer: A Double Sampling Transformer for Multivariate Time Series Long-term Prediction github: MTST 分类&#xff1a;多变量时间序列&#xff08;Multivariate time series&#xff09; 核心观点 多变量时间序列3个维度信息 …

Android AlertDialog对话框

目录 AlertDialog对话框普通对话框单选框多选框自定义框 AlertDialog对话框 部分节选自博主编《Android应用开发项目式教程》&#xff08;机械工业出版社&#xff09;2024.6 在Android中&#xff0c;AlertDialog弹出对话框用于显示一些重要信息或者需要用户交互的内容。 弹出…

【Linux进阶】磁盘分区2——MBR和GPT

1.磁盘的分区 因为如果你的磁盘被划分成两个分区&#xff0c;那么每个分区的设备文件名是什么&#xff1f; 在了解这个问题之前&#xff0c;我们先来复习一下磁盘的组成&#xff0c;因为现今磁盘的划分与它物理的组成很有关系。 我们谈过磁盘主要由碟片、机械手臂、磁头与主轴马…

gda动态调试-cnblog

忽的发现gda有动态调试功能 动态监听返回值 框柱指定方法&#xff0c;选择调试方法&#xff0c;gda会自动监听函数的返回值&#xff0c;例如 自定义frida脚本 gda会自动生成hook该函数的frida脚本

window.ai 开启你的内置AI之旅

❝ 成功是得你所想&#xff0c;幸福是享你所得 大家好&#xff0c;我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder ❝ 此篇文章所涉及到的技术有 AI( Gemini Nano) Chrome Ollama 因为&#xff0c;行文字数所限&#xff0c;有些概念可能会一带而过亦或者提供…

顶顶通呼叫中心中间件-外呼通道变量同步到坐席通道变量(mod_cti基于Freeswitch)

机器人伴随转人工或者排队转人工 把外呼通道同步到坐席通道变量 在拨号方案转人工动作cti_acd,或者转机器人动作cti_rotobt的前面&#xff0c;添加一个 export nolocal:变量名${变量名} 一、配置拨号方案 win-ccadmin配置方法 点击拨号方案 -> 点击进入排队 -> 根据图…

Java项目:基于SSM框架实现的中小企业人力资源管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的中小企业人力资源管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简…

jmeter-beanshell学习2-beanshell断言

继续写&#xff0c;之前写了获取变量&#xff0c;设置变量&#xff0c;今天先写个简单点的断言。 一般情况用响应断言&#xff0c;就挺好使&#xff0c;但是自动化还要生成报告&#xff0c;如果断言失败了&#xff0c;要保存结果&#xff0c;只能用beanshell处理&#xff0c;顺…

Ubuntu 24.04-自动安装-Nvidia驱动

教程 但在安全启动模式下可能会报错。 先在Nvidia官网找到GPU对应的驱动版&#xff0c; 1. 在软件与更新中选择合适的驱动 2. ubuntu自动安装驱动 sudo ubuntu-drivers autoinstall显示驱动 ubuntu-drivers devices3. 安装你想要的驱动 sudo apt install nvidia-driver-ve…

如何在 SwiftUI 中熟练使用 sensoryFeedback 修饰符

文章目录 前言背景介绍平台支持仅支持watchOS支持watchOS和iOS 基本用法预定义样式根据触发器值选择样式使用场景当值更改时触发使用条件闭包触发使用反馈闭包触发 可以运行 Demo总结 前言 SwiftUI 引入了新的 sensoryFeedback 视图修饰符&#xff0c;使我们能够在所有 Apple …