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

前端代码

html 复制代码
<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接收上传文件

py 复制代码
# -*- 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 logger

app = 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)
相关推荐
web1478621072311 分钟前
C# .Net Web 路由相关配置
前端·c#·.net
m0_7482478012 分钟前
Flutter Intl包使用指南:实现国际化和本地化
前端·javascript·flutter
飞的肖15 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
青灯文案123 分钟前
前端 HTTP 请求由 Nginx 反向代理和 API 网关到后端服务的流程
前端·nginx·http
m0_7482548828 分钟前
DataX3.0+DataX-Web部署分布式可视化ETL系统
前端·分布式·etl
ZJ_.39 分钟前
WPSJS:让 WPS 办公与 JavaScript 完美联动
开发语言·前端·javascript·vscode·ecmascript·wps
GIS开发特训营44 分钟前
Vue零基础教程|从前端框架到GIS开发系列课程(七)响应式系统介绍
前端·vue.js·前端框架·gis开发·webgis·三维gis
Cachel wood1 小时前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
学代码的小前端1 小时前
0基础学前端-----CSS DAY9
前端·css
joan_851 小时前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui