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

 5732

这篇文章主要介绍了vue+flask实现视频合成功能(拖拽上传),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

vue+flask实现视频合成

效果如下


在这里插入图片描述

原理就是 监听drop事件 来获取拖拽的文件列表


在这里插入图片描述

在这里插入图片描述

上传文件

通过axios 上传文件

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

  1. let files = this.fileList;
  2. let formd = new FormData();
  3. let i = 1;
  4. //添加上传列表
  5. files.forEach(item => {
  6.     formd.append(+ "", item, item.name)
  7.     i++;
  8. })
  9. formd.append("type", i)
  10. let config = {
  11.     headers: {
  12.         "Content-Type": "multipart/form-data"
  13.     }
  14. }
  15. //上传文件请求
  16. axios.post("/qwe", formd, config).then(res => {
  17.     console.log(res.data)
  18. })

flask处理文件

完整代码见最底部

逻辑如下

接收文件

为每次合成请求随机生成一个文件夹 临时保存文件

拼接视频

返回文件路径

  1. @app.route("/file",methods=['POST'])
  2. def test():
  3. #获取文件
  4. files = request.files
  5. #合成队列
  6. videoL = []
  7. #随机字符串
  8. dirs = sjs()
  9. #生成文件夹
  10. os.mkdir(dirs)
  11. #保存文件并添加至合成队列
  12. for file in files.values():
  13.     print(file)
  14.     dst = dirs + "/" + file.name + ".mp4"
  15.     file.save(dst)
  16.     video = VideoFileClip(dirs + "/" + file.name + ".mp4")
  17.     videoL.append(video)
  18.  
  19. #拼接视频
  20. final = concatenate_videoclips(videoL)
  21. #文件路径
  22. fileName = dirs + "/" +"{}.mp4".format(sjs())
  23. #生成视频
  24. final.to_videofile(fileName)
  25.  
  26. #销毁文件夹
  27. def sc():
  28.     shutil.rmtree(dirs)
  29.  
  30. #30秒后销毁文件夹
  31. timer = threading.Timer(30, sc)
  32. timer.start()
  33. # 返回文件路径
  34. return fileName

拼接获取文件路径

首先我们看flask

逻辑如下

通过文件名 获取文件 返回文件

  1. app.route("/getvoi",methods=['GET'])
  2. def getImg():
  3. #获取文件名
  4. ss = request.args['name']
  5. #文件加至返回响应
  6. response = make_response(
  7.     send_file(ss))
  8. #删除文件
  9. def sc():
  10.     os.remove(ss)
  11.  
  12. #30秒后删除文件
  13. timer = threading.Timer(30, sc)
  14. timer.start()
  15.  
  16. return response

前端获取

通过a标签下载

  1. <a s:href="herfs" rel="external nofollow" rel="external nofollow" :download="fileName">下载</a>

herfs如下


在这里插入图片描述


我们上传文件后 通过falsk处理返回文件路径 拼接后获取文件地址

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

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

配置代理说明

配置代理是为了解决跨域问题 开发环境可在vue.config.js配置即可使用

生产环境需要额外配置nginx


在这里插入图片描述


/qwe实际上就是 http://127.0.0.1:8087/file

/voi实际上就是 http://127.0.0.1:8087/getvoi

对应我们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 用于随机字符串生成

  1. import random
  2. import hashlib
  3. def sjs():
  4.     a = random.randint(0, 100)
  5.     a = "a" + str(a);
  6.     b = random.randint(100, 10000);
  7.     b = "b" + str(b);
  8.     c = hashlib.md5(a.encode(encoding='UTF-8')).hexdigest() + hashlib.md5(b.encode(encoding='UTF-8')).hexdigest();
  9.     c = "c" + str(c);
  10.     d = random.randint(10, 100);
  11.     d = "d" + str(d);
  12.     e = hashlib.md5(c.encode(encoding='UTF-8')).hexdigest() + hashlib.md5(d.encode(encoding='UTF-8')).hexdigest();
  13.     e = hashlib.md5(e.encode(encoding='UTF-8')).hexdigest()
  14.     return e;
app_service.py 服务代码
  1. from flask import Flask,request,send_file,make_response
  2. import os,json,threading,shutil
  3. from moviepy.editor import *
  4. from md5random import sjs
  5. app = Flask(__name__)
  6. @app.route("/file",methods=['POST'])
  7. def test():
  8.     #获取文件
  9.     files = request.files
  10.     #合成队列
  11.     videoL = []
  12.     #随机字符串
  13.     dirs = sjs()
  14.     #生成文件夹
  15.     os.mkdir(dirs)
  16.     #保存文件并添加至合成队列
  17.     for file in files.values():
  18.         print(file)
  19.         dst = dirs + "/" + file.name + ".mp4"
  20.         file.save(dst)
  21.         video = VideoFileClip(dirs + "/" + file.name + ".mp4")
  22.         videoL.append(video)
  23.     #拼接视频
  24.     final = concatenate_videoclips(videoL)
  25.     #文件路径
  26.     fileName = dirs + "/" +"{}.mp4".format(sjs())
  27.     #生成视频
  28.     final.to_videofile(fileName)
  29.     #销毁文件夹
  30.     def sc():
  31.         shutil.rmtree(dirs)
  32.     #30秒后销毁文件夹
  33.     timer = threading.Timer(30, sc)
  34.     timer.start()
  35.     # 返回文件路径
  36.     return fileName
  37. @app.route("/getvoi",methods=['GET'])
  38. def getImg():
  39.     #获取文件名
  40.     ss = request.args['name']
  41.     #文件加至返回响应
  42.     response = make_response(
  43.         send_file(ss))
  44.     #删除文件
  45.     def sc():
  46.         os.remove(ss)
  47.     #30秒后删除文件
  48.     timer = threading.Timer(30, sc)
  49.     timer.start()
  50.     return response
  51. if __name__ == '__main__':
  52.     app.run(host='0.0.0.0',port=8087)

vue代码

演示文件代码

  1. <template>
  2.     <div>
  3.         <div v-on:dragover="tts" v-on:drop="ttrs" style="width: 800px;height: 200px;border: 1px solid black;font-size: 40px;line-height: 200px">
  4.             {{ dt }}
  5.         </div>
  6.         <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">
  7.             <p style="font-size: 20px;float: left;position: relative;left: 20pxword-wrap:break-word;word-break:normal;">
  8.                 {{ item.name }}
  9.             </p>
  10.             <h5 style="float:right;position: absolute;top: 80px;right: 20px">
  11.                 {{ item.type }}
  12.             </h5>
  13.             <h6 style="position: absolute;top: 80px;float: left;left: 20px">
  14.                 {{ item.size | sizeType }}
  15.             </h6>
  16.             <button style="float: right" @click="del(index)">删除</button>
  17.         </div>
  18.         <!-- 此处为展示最后一个上传的文件 -->
  19.         <!-- <div style="position:relative;top: 100px">-->
  20.         <!--    <img v-if="isImage" :src="srcs" style="width: 800px" />-->
  21.         <!--    <video v-if="isVideo" controls :src="srcs" style="width: 800px"></video>-->
  22.         <!--    <audio v-if="isAudio" controls :src="srcs" style="width: 800px"></audio>-->
  23.         <!-- </div>-->
  24.         <el-button style="position: relative;top: 50px" type="success" @click="ups()" :disabled="!isCan">合成</el-button>
  25.         <el-button style="position: relative;top: 50px" v-loading="loading" type="success" >。。。</el-button>
  26.         <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>
  27.         <div style="position: relative;top: 100px">文件下载有效时间{{times}}s</div>
  28.     </div>
  29. </template>
  30. <script>
  31. import axios from "axios";
  32. export default {
  33.     name: "trs",
  34.     data() {
  35.         return {
  36.             dt: "",//上传提醒 "拖动到此处上传文件“或者"上传完成,可继续上传"
  37.             fileList: [],//文件列表
  38.             loading:false,
  39.             srcs: "",//图片/视频/音频 base64
  40.             isImage: false,//是否是图片
  41.             isAudio: false,//是否是音频
  42.             isVideo: false,//是否是视频
  43.             isCan: true,//是否能合成
  44.             isCans:true,//是否能下载
  45.             herfs: "",//下载地址
  46.             fileName: "",//文件名
  47.             times: 25//下载有效时间
  48.         };
  49.     },
  50.     filters: {
  51.         //格式化文件大小
  52.         sizeType(val) {
  53.             let kbs = val / 1024;
  54.             let mbs = 0;
  55.             let gbs = 0;
  56.             if (kbs >= 1024) {
  57.                 mbs = kbs / 1024;
  58.             }
  59.             if (mbs >= 1024) {
  60.                 gbs = mbs / 1024;
  61.                 return gbs.toFixed(2) + "GB";
  62.             } else if (mbs >= 1) {
  63.                 return mbs.toFixed(2) + "MB";
  64.             } else {
  65.                 return kbs.toFixed(2) + "KB";
  66.             }
  67.         }
  68.     },
  69.     mounted() {
  70.         let vm = this;
  71.         window.addEventListener("dragdrop", this.testfunc, false);
  72.         //全局监听 当页面内有文件拖动 提醒拖动到此处
  73.         document.addEventListener("dragover", function() {
  74.             console.log(111);
  75.             vm.dt = "拖动到此处上传文件";
  76.             console.log(vm.dt);
  77.         });
  78.     },
  79.     methods: {
  80.         //展示文件 主要为三个类型 图片/视频/音频
  81.         readFile(file) {
  82.             let vm = this;
  83.             let reader = new FileReader();
  84.             reader.readAsDataURL(file);
  85.             reader.onload = function() {
  86.                 let type = file.type.substr(0, 5);
  87.                 if (type == "image") {
  88.                     vm.isImage = true;
  89.                     vm.isAudio = false;
  90.                     vm.isVideo = false;
  91.                 } else if (type == "audio") {
  92.                     vm.isImage = false;
  93.                     vm.isAudio = true;
  94.                     vm.isVideo = false;
  95.                 } else if (type == "video") {
  96.                     vm.isImage = false;
  97.                     vm.isAudio = false;
  98.                     vm.isVideo = true;
  99.                 } else {
  100.                     alert("不是图片/视频/音频");
  101.                 }
  102.                 vm.srcs = reader.result;
  103.                 // this.$nextTick(()=>{
  104.                 //
  105.                 // })
  106.             };
  107.         },
  108.         //全局监听drop的触发事件 取消drop弹窗显示资源
  109.         testfunc(event) {
  110.             alert("dragdrop!");
  111.             //取消drop弹窗显示资源
  112.             event.stopPropagation();
  113.             event.preventDefault();
  114.         },
  115.         del(index) {
  116.             this.fileList.splice(index, 1);
  117.             if (this.fileList.length === 0) {
  118.                 this.dt = "";
  119.             }
  120.         },
  121.         //监听div上传框 当有文件拖动时 显示"拖动到此处上传文件"
  122.         tts(e) {
  123.             console.log(e);
  124.             this.dt = "拖动到此处上传文件";
  125.         },
  126.         //监听div上传框 drop事件触发
  127.         ttrs(e) {
  128.             console.log(e);
  129.             console.log(e.dataTransfer.files);
  130.             //获取文件
  131.             let datas = e.dataTransfer.files;
  132.             //取消drop弹窗显示资源
  133.             e.stopPropagation();
  134.             e.preventDefault();
  135.             datas.forEach(item => {
  136.                 if(item.type=="video/mp4"){
  137.                     this.fileList.push(item);
  138.                 }
  139.             });
  140.             //读取文件 如果不想展示图片/视频/音频可忽略
  141.             this.readFile(this.fileList[this.fileList.length - 1]);
  142.             this.dt = "上传完成,可继续上传";
  143.         },
  144.         //上传文件到服务器
  145.         ups(){
  146.             if(this.fileList.length==0){
  147.                 this.$message('文件列表为空');
  148.                 return ;
  149.             }
  150.             this.loading = true;
  151.             this.isCan = false;
  152.             this.isCans = true;
  153.             let files = this.fileList;
  154.             let formd = new FormData();
  155.             let i = 1;
  156.         //添加上传列表
  157.     files.forEach(item=>{
  158.         formd.append(i+"",item,item.name)
  159.             i++;
  160.         })
  161.         formd.append("type",i)
  162.         let config={
  163.             headers:{"Content-Type":"multipart/form-data"}
  164.         }
  165.         //上传文件请求
  166.         axios.post("/qwe",formd,config).then(res=>{
  167.             console.log(res.data)
  168.             this.loading = false
  169.             //合成下载路径
  170.             this.herfs = "/voi?name="+res.data
  171.             this.fileName = res.data.split('/')[1]
  172.             //禁止合成
  173.             this.isCan = false
  174.             this.isCans = false
  175.             //设置下载有效时间 时间到后无法下载但可以继续合成
  176.             let timer = setInterval(()=>{
  177.                 this.times--;
  178.             },1000)
  179.             this.setCans(timer)
  180.         })
  181.     },
  182.     setCans(timer){
  183.         setTimeout(()=>{
  184.             this.isCans = true
  185.             this.isCan = true
  186.             this.fileName =""
  187.             clearInterval(timer)
  188.             this.times = 25
  189.             },25000)
  190.         }
  191.     }
  192. };
  193. </script>
  194. <style scoped></style>

vue.config.js

  1. module.exports = {
  2.     devServer: {
  3.         // assetsSubDirectory: 'static',
  4.         // assetsPublicPath: '/',
  5.         proxy: {
  6.             "/qwe": {
  7.                 target: "http://127.0.0.1:8087/file",
  8.                 changeOrigin: true,
  9.                 pathRewrite: {
  10.                     "^/qwe": ""
  11.                 }
  12.             },
  13.             "/voi": {
  14.                 target: "http://127.0.0.1:8087/getvoi",
  15.                 changeOrigin: true,
  16.                 pathRewrite: {
  17.                     "^/voi": ""
  18.                 }
  19.             }
  20.         }
  21.     }
  22. };


本文网址:https://www.zztuku.com/detail-8739.html
站长图库 - vue+flask实现视频合成功能(拖拽上传)
申明:如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐