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)
相关推荐
桂月二二4 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
CodeClimb5 小时前
【华为OD-E卷 - 第k个排列 100分(python、java、c++、js、c)】
java·javascript·c++·python·华为od
沈梦研5 小时前
【Vscode】Vscode不能执行vue脚本的原因及解决方法
ide·vue.js·vscode
hunter2062065 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb5 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角5 小时前
CSS 颜色
前端·css
轻口味6 小时前
Vue.js 组件之间的通信模式
vue.js
浪浪山小白兔6 小时前
HTML5 新表单属性详解
前端·html·html5
lee5767 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm
2401_897579657 小时前
AI赋能Flutter开发:ScriptEcho助你高效构建跨端应用
前端·人工智能·flutter