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最新毕业设计选题推荐…

XSS 攻击是什么?如何防护?

1. 什么是 XSS 攻击 跨站脚本攻击&#xff0c;是一种很常见的网络安全威胁。 它允许攻击者在目标浏览器中注入恶意脚本代码。这些恶意脚本会执行多种非法操作。比如盗取你的 cookie&#xff0c;会话信息&#xff0c;篡改网页内容&#xff0c;重定向到别的恶意网站。控制浏览器…

零基础学习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;群内展示&…

JavaScript中的reduce()

reduce() 方法是 JavaScript 数组&#xff08;Array&#xff09;对象的一个非常强大的方法&#xff0c;它允许你对数组中的每个元素执行一个由你提供的 reducer 函数&#xff08;升序执行&#xff09;&#xff0c;将其结果汇总为单个返回值。这个方法非常适用于累加器、数组求和…

010 Volatile和本地线程

文章目录 关键字Volatile可见性&#xff1a;原子性&#xff1a;有序性&#xff1a; 本地线程如何创建ThreadLocal变量如何访问ThreadLocal变量关于InheritableThreadLocal 关键字Volatile Volatile是轻量级的synchronized,在多处理器环境下&#xff0c;可以保证共享变量的可见…

蝙蝠优化算法(Bat Algorithm,BA)及其Python和MATLAB实现

蝙蝠优化算法&#xff08;Bat Algorithm&#xff0c;简称BA&#xff09;是一种基于蝙蝠群体行为的启发式优化算法&#xff0c;由Xin-She Yang于2010年提出。该算法模拟了蝙蝠捕食时在探测目标、适应环境和调整自身位置等过程中的行为&#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个维度信息 …

Spring Cloud Alibaba组件概览

目录 Spring Cloud Alibaba组件概览引言一、Spring Cloud Alibaba概述二、Spring Cloud Alibaba组件概览2.1 Nacos2.1.1 概述2.1.2 特点2.1.3 应用场景2.1.4 实例分析 2.2 Sentinel2.2.1 概述2.2.2 特点2.2.3 应用场景2.2.4 实例分析 2.3 RocketMQ2.3.1 概述2.3.2 特点2.3.3 应…

Android AlertDialog对话框

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

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

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

Go 语言 UUID 库 google/uuid 源码解析:时钟信息

google/uuid 库地址 google/uuid 时间相关的部分汇聚在 uuid 包下的 time.go 文件中。 UUID 的 RFC 4122 变体中的版本1和版本2依赖于时钟信息&#xff0c;所以 uuid 库将时钟信息的实现定义在本文件中&#xff0c;供对应版本 UUID 的生成使用。 UUID 依赖于时钟信息的实现版…

5G NR与4G LTE的技术差异

5G NR与4G LTE的技术差异 5G与4G相比&#xff0c;5G(NR)技术有以下优势点&#xff1a; 一、总体技术方面 系统消息 4G(LTE): 支持在任何条件(或情况)下始终开启所有系统消息的广播&#xff0c;导致大量资源浪费&#xff0c;且终端(UE)需要持续评估。 系统信息广播是终端(UE)…