vue+flask实现视频合成功能(拖拽上传)
这篇文章主要介绍了vue+flask实现视频合成功能(拖拽上传),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
vue+flask实现视频合成
效果如下
原理就是 监听drop事件 来获取拖拽的文件列表
上传文件
通过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如下
我们上传文件后 通过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 用于随机字符串生成
app_service.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;
- 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": ""
- }
- }
- }
- }
- };
本文网址:https://www.zztuku.com/detail-8739.html
站长图库 - vue+flask实现视频合成功能(拖拽上传)
申明:如有侵犯,请 联系我们 删除。
您还没有登录,请 登录 后发表评论!
提示:请勿发布广告垃圾评论,否则封号处理!!