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实现购物小球抛物线的方法实例
Nov 22 Vue.js
vue单元格多列合并的实现
Nov 26 Vue.js
vue中配置scss全局变量的步骤
Dec 28 Vue.js
Vue实现随机验证码功能
Dec 29 Vue.js
Vue常用API、高级API的相关总结
Feb 02 Vue.js
如何管理Vue中的缓存页面
Feb 06 Vue.js
详解vue3中组件的非兼容变更
Mar 03 Vue.js
如何让vue长列表快速加载
Mar 29 Vue.js
vue实现简单数据双向绑定
Apr 28 Vue.js
一文带你理解vue创建一个后台管理系统流程(Vue+Element)
May 18 Vue.js
详解Vue3使用axios的配置教程
Apr 29 Vue.js
vue-cli3.x配置全局的scss的时候报错问题及解决
Apr 30 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
[FAQ]PHP中的一些常识:类篇
2006/10/09 PHP
php fputcsv命令 写csv文件遇到的小问题(多维数组连接符)
2011/05/24 PHP
redis 队列操作的例子(php)
2012/04/12 PHP
浅析PHP绘图技术
2013/07/03 PHP
php获取参数的几种方法总结
2014/02/18 PHP
PHP实现扎金花游戏之大小比赛的方法
2015/03/10 PHP
表单提交错误后返回内容消失问题的解决方法(PHP网站)
2015/10/20 PHP
基于PHP实现短信验证码接口(容联运通讯)
2016/09/06 PHP
Javascript new关键字的玄机 以及其它
2010/08/25 Javascript
修改jquery.lazyload.js实现页面延迟载入
2010/12/22 Javascript
基于jquery的文章中所有图片width大小批量设置方法
2013/08/01 Javascript
javascript使用onclick事件改变选中行的颜色
2013/12/30 Javascript
javascript学习笔记(四)function函数部分
2014/09/30 Javascript
2016年最热门的15 款代码语法高亮工具,美化你的代码
2016/01/06 Javascript
JavaScript中的原型prototype完全解析
2016/05/10 Javascript
AngularJS ng-bind-html 指令详解及实例代码
2016/07/30 Javascript
如何利用node.js开发一个生成逐帧动画的小工具
2019/12/01 Javascript
webpack+express实现文件精确缓存的示例代码
2020/06/11 Javascript
解决Antd Table表头加Icon和气泡提示的坑
2020/11/17 Javascript
[04:44]DOTA2西游记战队视频彩蛋流出 师徒开黑巧遇林书豪
2016/08/03 DOTA
Python多线程学习资料
2012/12/19 Python
Python文档生成工具pydoc使用介绍
2015/06/02 Python
不可错过的十本Python好书
2017/07/06 Python
python和ruby,我选谁?
2017/09/13 Python
使用python将时间转换为指定的格式方法
2018/11/12 Python
解决TensorFlow训练内存不断增长,进程被杀死问题
2020/02/05 Python
PyCharm 在Windows的有用快捷键详解
2020/04/07 Python
Python 实现进度条的六种方式
2021/01/06 Python
canvas实现扭蛋机动画效果的示例代码
2018/10/17 HTML / CSS
欧洲最大的笔和书写专家:The Pen Shop
2017/03/19 全球购物
世界上最好的精品店:Shoptiques
2018/02/05 全球购物
个人教师自我评价范文
2013/12/02 职场文书
幼儿园教师师德师风演讲稿:我自豪我是一名幼师
2014/09/10 职场文书
2014年计划生育工作总结
2014/11/14 职场文书
工程部经理岗位职责
2015/02/02 职场文书
Django与数据库交互的实现
2021/06/03 Python