vue+flask实现视频合成(拖拽上传)

代码哈士奇 等级 558 0 0
标签: httpsJavascript

vue+flask实现视频合成 效果如下 vue+flask实现视频合成(拖拽上传) 欢迎访问博客代码哈士奇 技术 聊天 交流群 974178910 前端交流群 535620886

拖拽上传我们之前一个文章有写过

https://dmhsq.blog.csdn.net/article/details/114109519

原理就是 监听drop事件 来获取拖拽的文件列表 vue+flask实现视频合成(拖拽上传) vue+flask实现视频合成(拖拽上传)

上传文件

通过axios 上传文件

this,.fileList就是我们的文件列表

let files = this.fileList;
let formd = new FormData();
let i = 1;

//添加上传列表
files.forEach(item => {
    formd.append(i + "", item, item.name)
    i++;
})
formd.append("type", i)
let config = {
    headers: {
        "Content-Type": "multipart/form-data"
    }
}

//上传文件请求
axios.post("/qwe", formd, config).then(res => {
    console.log(res.data)
})

flask处理文件

完整代码见最底部

逻辑如下 接收文件 为每次合成请求随机生成一个文件夹 临时保存文件
拼接视频 返回文件路径

@app.route("/file",methods=['POST'])
def test():

    #获取文件
    files =  request.files
    #合成队列
    videoL = []
    #随机字符串
    dirs = sjs()
    #生成文件夹
    os.mkdir(dirs)
    #保存文件并添加至合成队列
    for file in files.values():
        print(file)
        dst = dirs + "/" + file.name + ".mp4"
        file.save(dst)
        video = VideoFileClip(dirs + "/" + file.name + ".mp4")
        videoL.append(video)

    #拼接视频
    final = concatenate_videoclips(videoL)
    #文件路径
    fileName = dirs + "/" +"{}.mp4".format(sjs())
    #生成视频
    final.to_videofile(fileName)

    #销毁文件夹
    def sc():
        shutil.rmtree(dirs)

    #30秒后销毁文件夹
    timer = threading.Timer(30, sc)
    timer.start()

    # 返回文件路径
    return fileName

拼接获取文件路径

首先我们看flask

逻辑如下 通过文件名 获取文件 返回文件

@app.route("/getvoi",methods=['GET'])
def getImg():
    #获取文件名
    ss = request.args['name']
    #文件加至返回响应
    response = make_response(
        send_file(ss))

    #删除文件
    def sc():
        os.remove(ss)

    #30秒后删除文件
    timer = threading.Timer(30, sc)
    timer.start()

    return response

前端获取

通过a标签下载

<a s :href="herfs" :download="fileName">下载</a>

herfs如下 vue+flask实现视频合成(拖拽上传) 我们上传文件后 通过falsk处理返回文件路径 拼接后获取文件地址

a标签添加download属性可以给下载的文件命名

如果你对/qwe /voi有疑惑 请看下面的配置代理说明

配置代理说明

配置代理是为了解决跨域问题 开发环境可在vue.config.js配置即可使用 生产环境需要额外配置nginx vue+flask实现视频合成(拖拽上传)/qwe实际上就是 http://127.0.0.1:8087/file /voi实际上就是 http://127.0.0.1:8087/getvoi 对应我们flask中的

vue+flask实现视频合成(拖拽上传)

额外说明(如果你使用uni-app)

如果你使用uni-app 可参照文档使用api 上传文件api https://uniapp.dcloud.io/api/request/network-file?id=uploadfile 下载文件api https://uniapp.dcloud.io/api/request/network-file?id=downloadfile 或者直接使用别人封装好的 插件毕竟比较方便

完整代码

如果你不想一个一个复制可以去下载 下载途径1: https://download.csdn.net/download/qq_42027681/15561897 下载途径2:https://github.com/dmhsq/vue-flask-videoSynthesis

flask代码

md5random.py 用于随机字符串生成

import random
import hashlib
def sjs():
    a = random.randint(0, 100)
    a = "a" + str(a);
    b = random.randint(100, 10000);
    b = "b" + str(b);
    c = hashlib.md5(a.encode(encoding='UTF-8')).hexdigest() + hashlib.md5(b.encode(encoding='UTF-8')).hexdigest();
    c = "c" + str(c);
    d = random.randint(10, 100);
    d = "d" + str(d);
    e = hashlib.md5(c.encode(encoding='UTF-8')).hexdigest() + hashlib.md5(d.encode(encoding='UTF-8')).hexdigest();
    e = hashlib.md5(e.encode(encoding='UTF-8')).hexdigest()
    return e;

app_service.py 服务代码

from flask import Flask,request,send_file,make_response
import os,json,threading,shutil
from moviepy.editor import *
from md5random import sjs

app = Flask(__name__)

@app.route("/file",methods=['POST'])
def test():

    #获取文件
    files =  request.files
    #合成队列
    videoL = []
    #随机字符串
    dirs = sjs()
    #生成文件夹
    os.mkdir(dirs)
    #保存文件并添加至合成队列
    for file in files.values():
        print(file)
        dst = dirs + "/" + file.name + ".mp4"
        file.save(dst)
        video = VideoFileClip(dirs + "/" + file.name + ".mp4")
        videoL.append(video)

    #拼接视频
    final = concatenate_videoclips(videoL)
    #文件路径
    fileName = dirs + "/" +"{}.mp4".format(sjs())
    #生成视频
    final.to_videofile(fileName)

    #销毁文件夹
    def sc():
        shutil.rmtree(dirs)

    #30秒后销毁文件夹
    timer = threading.Timer(30, sc)
    timer.start()

    # 返回文件路径
    return fileName


@app.route("/getvoi",methods=['GET'])
def getImg():
    #获取文件名
    ss = request.args['name']
    #文件加至返回响应
    response = make_response(
        send_file(ss))

    #删除文件
    def sc():
        os.remove(ss)

    #30秒后删除文件
    timer = threading.Timer(30, sc)
    timer.start()

    return response

if __name__ == '__main__':
    app.run(host='0.0.0.0',port=8087)

vue代码

演示文件代码

<template>
  <div>
    <div
      v-on:dragover="tts"
      v-on:drop="ttrs"
      style="width: 800px;height: 200px;border: 1px solid black;font-size: 40px;line-height: 200px"
    >
      {{ dt }}
    </div>
    <div
      v-for="(item, index) in fileList"
      :key="index"
      style="width: 800px;height: 200px;border: 1px solid black;font-size: 40px;position: relative;top:10px"
    >
      <p
        style="font-size: 20px;float: left;position: relative;left: 20pxword-wrap:break-word;word-break:normal;"
      >
        {{ item.name }}
      </p>
      <h5 style="float:right;position: absolute;top: 80px;right: 20px">
        {{ item.type }}
      </h5>
      <h6 style="position: absolute;top: 80px;float: left;left: 20px">
        {{ item.size | sizeType }}
      </h6>
      <button style="float: right" @click="del(index)">删除</button>
    </div>
    <!-- 此处为展示最后一个上传的文件 -->
<!--    <div style="position:relative;top: 100px">-->
<!--      <img v-if="isImage" :src="srcs" style="width: 800px" />-->
<!--      <video v-if="isVideo" controls :src="srcs" style="width: 800px"></video>-->
<!--      <audio v-if="isAudio" controls :src="srcs" style="width: 800px"></audio>-->
<!--    </div>-->

    <el-button style="position: relative;top: 50px"  type="success" @click="ups()" :disabled="!isCan">合成</el-button>
    <el-button style="position: relative;top: 50px" v-loading="loading" type="success" >。。。</el-button>
    <a style="position: relative;top: 50px;left: 15px;" type="success" :href="herfs" :download="fileName"><el-button :disabled="isCans"><span style="color: black">下载</span></el-button></a>
    <div style="position: relative;top: 100px">文件下载有效时间{{times}}s</div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "trs",
  data() {
    return {
      dt: "",//上传提醒 "拖动到此处上传文件“或者"上传完成,可继续上传"
      fileList: [],//文件列表
      loading:false,
      srcs: "",//图片/视频/音频 base64
      isImage: false,//是否是图片
      isAudio: false,//是否是音频
      isVideo: false,//是否是视频
      isCan: true,//是否能合成
      isCans:true,//是否能下载
      herfs: "",//下载地址
      fileName: "",//文件名
      times: 25//下载有效时间
    };
  },
  filters: {
    //格式化文件大小
    sizeType(val) {
      let kbs = val / 1024;
      let mbs = 0;
      let gbs = 0;
      if (kbs >= 1024) {
        mbs = kbs / 1024;
      }
      if (mbs >= 1024) {
        gbs = mbs / 1024;
        return gbs.toFixed(2) + "GB";
      } else if (mbs >= 1) {
        return mbs.toFixed(2) + "MB";
      } else {
        return kbs.toFixed(2) + "KB";
      }
    }
  },
  mounted() {
    let vm = this;
    window.addEventListener("dragdrop", this.testfunc, false);

    //全局监听 当页面内有文件拖动 提醒拖动到此处
    document.addEventListener("dragover", function() {
      console.log(111);
      vm.dt = "拖动到此处上传文件";
      console.log(vm.dt);
    });
  },
  methods: {
    //展示文件 主要为三个类型 图片/视频/音频
    readFile(file) {
      let vm = this;
      let reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function() {
        let type = file.type.substr(0, 5);
        if (type == "image") {
          vm.isImage = true;
          vm.isAudio = false;
          vm.isVideo = false;
        } else if (type == "audio") {
          vm.isImage = false;
          vm.isAudio = true;
          vm.isVideo = false;
        } else if (type == "video") {
          vm.isImage = false;
          vm.isAudio = false;
          vm.isVideo = true;
        } else {
          alert("不是图片/视频/音频");
        }
        vm.srcs = reader.result;
        // this.$nextTick(()=>{
        //
        // })
      };
    },
    //全局监听drop的触发事件 取消drop弹窗显示资源
    testfunc(event) {
      alert("dragdrop!");

      //取消drop弹窗显示资源
      event.stopPropagation();
      event.preventDefault();
    },
    del(index) {
      this.fileList.splice(index, 1);
      if (this.fileList.length === 0) {
        this.dt = "";
      }
    },
    //监听div上传框 当有文件拖动时 显示"拖动到此处上传文件"
    tts(e) {
      console.log(e);
      this.dt = "拖动到此处上传文件";
    },
    //监听div上传框 drop事件触发
    ttrs(e) {
      console.log(e);
      console.log(e.dataTransfer.files);

      //获取文件
      let datas = e.dataTransfer.files;

      //取消drop弹窗显示资源
      e.stopPropagation();
      e.preventDefault();
      datas.forEach(item => {
        if(item.type=="video/mp4"){
          this.fileList.push(item);
        }
      });

      //读取文件 如果不想展示图片/视频/音频可忽略
      this.readFile(this.fileList[this.fileList.length - 1]);



      this.dt = "上传完成,可继续上传";
    },

    //上传文件到服务器
    ups(){
      if(this.fileList.length==0){
        this.$message('文件列表为空');
        return ;
      }
      this.loading = true;
      this.isCan = false;
      this.isCans = true;
      let files = this.fileList;
      let formd = new FormData();
      let i = 1;

      //添加上传列表
      files.forEach(item=>{
        formd.append(i+"",item,item.name)
        i++;
      })
      formd.append("type",i)
      let config={
        headers:{"Content-Type":"multipart/form-data"}
      }

      //上传文件请求
      axios.post("/qwe",formd,config).then(res=>{
        console.log(res.data)
        this.loading = false
        //合成下载路径
        this.herfs = "/voi?name="+res.data

        this.fileName = res.data.split('/')[1]
        //禁止合成
        this.isCan = false

        this.isCans = false

        //设置下载有效时间 时间到后无法下载但可以继续合成
        let timer = setInterval(()=>{
          this.times--;
        },1000)
        this.setCans(timer)
      })
    },
    setCans(timer){
      setTimeout(()=>{
        this.isCans = true
        this.isCan = true
        this.fileName =""
        clearInterval(timer)
        this.times = 25
      },25000)
    }
  }
};
</script>

<style scoped></style>

vue.config.js

module.exports = {
  devServer: {
    // assetsSubDirectory: 'static',
    // assetsPublicPath: '/',
    proxy: {
      "/qwe": {
        target: "http://127.0.0.1:8087/file",
        changeOrigin: true,
        pathRewrite: {
          "^/qwe": ""
        }
      },
      "/voi": {
        target: "http://127.0.0.1:8087/getvoi",
        changeOrigin: true,
        pathRewrite: {
          "^/voi": ""
        }
      }
    }
  }
};
收藏
评论区

相关推荐

What the f*ck JavaScript?
What the fck JavaScript? 一个有趣和棘手的 JavaScript 示例列表。 JavaScript 是一种很好的语言。
Canvas入门实战之用javascript面向对象实现一个图形验证码
本文主要介绍用canvas实现图形验证码的一些思路以及如何用javascript面向对象的方式更友好的实现canvas的功能,关于canvas的一些基本使用方法和API我整理了一个思维导图,大家感兴趣的可以参考学习。 (https://imghelloworld.osscnbeijing.aliyuncs.com/imgs/b158fad95
《前端实战总结》之使用解释器模式实现获取元素Xpath路径的算法
前端领域里基于javascript的设计模式和算法有很多,在很多复杂应用中也扮演着很重要的角色,接下来就介绍一下javascript设计模式中的解释器模式,并用它来实现一个获取元素Xpath路径的算法。 上期回顾 《前端实战总结》之迭代器模式的N1种应用场景(https://juejin.im/post/6844904008616771591)
Cors跨域解决
一、浏览器跨域问题产生 1、跨源资源共享(CORS)中文文档: https://developer.mozilla.org/zhCN/docs/Web/HTTP/Access_control_CORS 2、什么是浏览器跨域问题 指的是浏览器不能执行其他网站的脚本。JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象,即同源策略。就好比我
Hook 规则 – React
Hook 规则 _Hook_ 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。 Hook 本质就是 JavaScript 函数,但是在使用它时需要遵循两条规则。我们提供了一个 linter 插件(https://www.npmjs.com/package/
舒文:浅谈阿里前端的多样化
2007年,Jeff Atwood 提出了一个著名的观点, 戏谑又似认真地称其为 Atwood's Law(https://blog.codinghorror.com/theprincipleofleastpower/): _any application that can be written in JavaScript, will event
一文搞懂什么是HTTP与HTTPS
(https://blog.csdn.net/petterp/article/details/102779257)Http与Https的区别。 在最近的开发中,深感网络相关基础知识薄弱,于是趁周末好好总结一
20 张图彻底弄懂 HTTPS 的原理
前言 近年来各大公司对信息安全传输越来越重视,也逐步把网站升级到 HTTPS 了,那么大家知道 HTTPS 的原理是怎样的吗,到底是它是如何确保信息安全传输的?网上挺多介绍 HTTPS,但我发现总是或多或少有些点有些遗漏,没有讲全,今天试图由浅入深地把 HTTPS 讲明白,相信大家看完一定能掌握 HTTPS 的原理,本文大纲如下: HTTP 为什么不安全
巨大提升!更快的 async 函数和 promises
(https://imghelloworld.osscnbeijing.aliyuncs.com/669a1c8f7203559afa4621628303674c.png) 翻译自:Faster async functions and promises(https://v8.dev/blog/fastasync) JavaScript
JavaScript中本地存储的方式有哪些?
(https://imghelloworld.osscnbeijing.aliyuncs.com/1f907f0895e2be23aa56604dd42e3626.png) 一、方式 javaScript本地缓存的方法我们主要讲述以下四种: cookie sessionStorage loc
如何在React Native和Expo中掩盖Text和TextInput组件
在本文中,我将向您展示如何在React Native和Expo中使用自定义蒙版,可用于iOS,Android和Web!我们将使用一个名为库,这是一个没有本机代码的完整javascript库,然后您可以在React Native环境的所有CLI中使用。](https://res.cloudinary.com/practicaldev/image/fetch/s
Webpack学习整理集锦【从最基础的demo入手,自己实现一个脚手架 】
前言本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。开源网址https://github.com/maomi
https://cloud.tencent.com/developer/article/write/1830331
一、目标今天的目标是这个sign和appcode 二、步骤 Jadx没法上了app加了某梆的企业版,Jadx表示无能为力了。 FRIDADEXDumpDexDump出来,木有找到有效的信息。 Wallbreaker葫芦娃的Wallbreaker可以做些带壳分析,不过这个样本,用Frida的Spawn模式可以载入,Attach模式会失败。而直接用Objecti
javascript实践教程-01-javascript介绍
本节目标1. 了解javascript是什么。2. 了解javascript能干什么。 内容摘要本篇介绍了javascript是什么,为什么要用javascript,ECMAScript标准是什么等。阅读时间大约510分钟。 javascript是什么?javascript是世界上最流行的脚本语言,因为你在电脑、手机、平板上浏览的所有的网页,以及无数基于HT
javascript实践教程-02-javascript入门
本节目标1. 掌握如何编写javascript代码。2. 掌握javascript的3个弹框。3. 掌握javascript的注释。4. 掌握浏览器的调试工具控制台。 内容摘要本篇介绍了如何在网页上编写js代码,如何引入外部js代码文件,js的3个弹框、注释语法,还有浏览器调试工具的控制台使用。阅读时间1520分钟。 script标签如果我们需要在网页中编写