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


Posted in Vue.js onMarch 04, 2021

vue+flask实现视频合成
效果如下

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

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

原理就是 监听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" rel="external nofollow" rel="external nofollow" :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" rel="external nofollow" rel="external nofollow" :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": ""
  }
  }
 }
 }
};

到此这篇关于vue+flask实现视频合成功能(拖拽上传)的文章就介绍到这了,更多相关vue视频合成内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Vue.js 相关文章推荐
vue+vant实现购物车全选和反选功能
Nov 17 Vue.js
基于vue+echarts数据可视化大屏展示的实现
Dec 25 Vue.js
vue中watch的用法汇总
Dec 28 Vue.js
Vue实现一种简单的无限循环滚动动画的示例
Jan 10 Vue.js
vue3 watch和watchEffect的使用以及有哪些区别
Jan 26 Vue.js
Vite和Vue CLI的优劣
Jan 30 Vue.js
手写Vue2.0 数据劫持的示例
Mar 04 Vue.js
vue实现倒计时功能
Mar 24 Vue.js
一篇文章学会Vue中间件管道
Jun 20 Vue.js
vue3使用vuedraggable实现拖拽功能
Apr 06 Vue.js
vue 数字翻牌器动态加载数据
Apr 20 Vue.js
vue实现省市区联动 element-china-area-data插件
Apr 22 Vue.js
vue打开新窗口并实现传参的图文实例
Mar 04 #Vue.js
Vue-router编程式导航的两种实现代码
Mar 04 #Vue.js
手写Vue2.0 数据劫持的示例
Mar 04 #Vue.js
vue3.0封装轮播图组件的步骤
Mar 04 #Vue.js
vue3.0 项目搭建和使用流程
Mar 04 #Vue.js
vue 数据双向绑定的实现方法
Mar 04 #Vue.js
vue3.0中使用element的完整步骤
Mar 04 #Vue.js
You might like
PHP开发中常用的字符串操作函数
2011/02/08 PHP
php模块memcache和memcached区别分析
2011/06/14 PHP
ThinkPHP3.1新特性之G方法的使用
2014/06/19 PHP
PHP使用Memcache时模拟命名空间及缓存失效问题的解决
2016/02/27 PHP
CodeIgniter基于Email类发邮件的方法
2016/03/29 PHP
详解PHP的Yii框架中的Controller控制器
2016/03/29 PHP
自制PHP框架之设计模式
2017/05/07 PHP
图片自动更新(说明)
2006/10/02 Javascript
js获取当前页面的url网址信息
2014/06/12 Javascript
自己动手手写jQuery插件总结
2015/01/20 Javascript
JavaScript学习笔记之定时器
2015/01/22 Javascript
jQuery使用attr()方法同时设置多个属性值用法实例
2015/03/26 Javascript
JavaScript jQuery 中定义数组与操作及jquery数组操作
2015/12/18 Javascript
jQuery Mobile中的button按钮组件基础使用教程
2016/05/23 Javascript
利用jquery实现实时更新歌词的方法
2017/01/06 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
微信小程序模板和模块化用法实例分析
2017/11/28 Javascript
浅谈Vuejs中nextTick()异步更新队列源码解析
2017/12/31 Javascript
手把手带你入门微信小程序新框架Kbone的使用
2020/02/25 Javascript
vue-router 2.0 跳转之router.push()用法说明
2020/08/12 Javascript
JavaScript中的Proxy对象
2020/11/27 Javascript
Python3读取zip文件信息的方法
2015/05/22 Python
Python读写文件方法总结
2015/06/09 Python
Python进阶之递归函数的用法及其示例
2018/01/31 Python
python2.7 json 转换日期的处理的示例
2018/03/07 Python
用TensorFlow实现多类支持向量机的示例代码
2018/04/28 Python
如何基于Python实现word文档重新排版
2020/09/29 Python
html5 touch事件实现页面上下滑动效果【附代码】
2016/03/10 HTML / CSS
小学生学习感言
2014/03/10 职场文书
销售会计岗位职责
2014/03/15 职场文书
考察现实表现材料
2014/05/19 职场文书
学术会议邀请函
2015/01/30 职场文书
中秋节随笔
2015/08/15 职场文书
公安忠诚教育心得体会
2016/01/23 职场文书
JS一分钟在github+Jekyll的博客中添加访问量功能的实现
2021/04/03 Javascript
OpenStack虚拟机快照和增量备份实现方法
2022/04/04 Servers