vue+flask實現視頻合成功能(拖拽上傳)

vue+flask實現視頻合成
效果如下

在這裡插入圖片描述

拖拽上傳我們之前一個文章有寫過

//www.jb51.net/article/206543.htm

原理就是 監聽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 用於隨機字符串生成

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視頻合成內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: