Vue+flaskで動画合成機能を実現(ドラッグ&ドロップアップロード)

Vue+flaskで動画合成機能を実現(ドラッグ&ドロップアップロード)

vue+flaskで実現したビデオ合成効果は以下のとおりです

ここに画像の説明を挿入

ドラッグアンドドロップアップロードについては以前の記事で書きました。

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

原理は、ドロップイベントをリッスンしてドラッグされたファイルリストを取得することです。

ここに画像の説明を挿入
ここに画像の説明を挿入

ファイルをアップロードする

axios経由でファイルをアップロードする

this.fileListはファイルリストです

ファイルを this.fileList とします。
formd = new FormData();
i = 1 とします。

//アップロードリストファイルを追加します。forEach(item => {
	formd.append(i + "", 項目、項目名)
	私は++;
})
formd.append("type", i)
設定 = {
	ヘッダー: {
		「コンテンツタイプ」: 「マルチパート/フォームデータ」
	}
}

//ファイルアップロードリクエスト axios.post("/qwe", formd, config).then(res => {
	コンソールログ(res.data)
})

Flaskはファイルを処理する

完全なコードは下部をご覧ください

ロジックは次のとおりです。ファイルを受信し、合成要求ごとにフォルダーをランダムに生成し、ファイルを一時的に保存し、ビデオをステッチし、ファイルパスを返します。

@app.route("/file",メソッド=['POST'])
デフテスト():

 #ファイルを取得する files = request.files
 #合成キュー videoL = []
 #ランダム文字列 dirs = sjs()
 #フォルダーを生成 os.mkdir(dirs)
 #ファイルを保存し、files.values() 内のファイルの合成キューに追加します。
  印刷(ファイル)
  dst = dirs + "/" + ファイル名 + ".mp4"
  ファイル.save(dst)
  ビデオ = VideoFileClip(dirs + "/" + ファイル名 + ".mp4")
  videoL.append(ビデオ)
 
 #ビデオを結合 final = concatenate_videoclips(videoL)
 #ファイルパス fileName = dirs + "/" + "{}.mp4".format(sjs())
 #ビデオを生成する final.to_videofile(fileName)
 
 #フォルダーを破棄 def sc():
  shutil.rmtree(ディレクトリ)
 
 #30秒後にフォルダーを破棄します timer = threading.Timer(30, sc)
 タイマー.開始()

 # ファイルパスを返す return fileName

ファイルパスを取得するためのスプライシング

まずフラスコを見てみましょう

ロジックは次のとおりです。ファイル名でファイルを取得し、ファイルを返します。

app.route("/getvoi",メソッド=['GET'])
getImg() を定義します:
 #ファイル名を取得する ss = request.args['name']
 #返されたレスポンスにファイルを追加する response = make_response(
  送信ファイル

 #ファイルを削除する def sc():
  os.remove(ss)
 
 #30秒後にファイルを削除します timer = threading.Timer(30, sc)
 タイマー.開始()
 
 応答を返す

フロントエンドの獲得

タグaでダウンロード

<as :href="herfs" rel="external nofollow" rel="external nofollow" :download="fileName">ダウンロード</a>

ハーフは以下の通りです

ここに画像の説明を挿入

ファイルをアップロードした後、返されたファイルパスをfalskで処理し、ファイルアドレスを取得します。

aタグにダウンロード属性を追加すると、ダウンロードしたファイルに名前を付けることができます。

/qwe /voiについてご質問がある場合は、次のプロキシ設定手順を参照してください。

プロキシ設定手順

プロキシを設定する目的は、クロスドメインの問題を解決することです。開発環境はvue.config.jsで設定して使用できます。本番環境ではnginxの追加設定が必要です。

ここに画像の説明を挿入

/qwe は実際には http://127.0.0.1:8087/file です
/voi は実際には http://127.0.0.1:8087/getvoi です
フラスコに対応

ここに画像の説明を挿入

追加メモ(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

フラスココード

md5random.pyはランダムな文字列を生成するために使用されます

ランダムにインポート
ハッシュライブラリをインポートする
sjs() を定義します:
 a = ランダム.randint(0, 100)
 a = "a" + str(a);
 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 = ランダム.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()
 e を返します。

app_service.py サービスコード

Flask から Flask、request、send_file、make_response をインポートします
os、json、threading、shutil をインポートします
moviepy.editor からインポート *
md5randomからsjsをインポート

アプリ = Flask(__name__)

@app.route("/file",メソッド=['POST'])
デフテスト():

 #ファイルを取得する files = request.files
 #合成キュー videoL = []
 #ランダム文字列 dirs = sjs()
 #フォルダーを生成 os.mkdir(dirs)
 #ファイルを保存し、files.values() 内のファイルの合成キューに追加します。
  印刷(ファイル)
  dst = dirs + "/" + ファイル名 + ".mp4"
  ファイル.save(dst)
  ビデオ = VideoFileClip(dirs + "/" + ファイル名 + ".mp4")
  videoL.append(ビデオ)

 #ビデオを結合 final = concatenate_videoclips(videoL)
 #ファイルパス fileName = dirs + "/" + "{}.mp4".format(sjs())
 #ビデオを生成する final.to_videofile(fileName)

 #フォルダーを破棄 def sc():
  shutil.rmtree(ディレクトリ)

 #30秒後にフォルダーを破棄します timer = threading.Timer(30, sc)
 タイマー.開始()

 # ファイルパスを返す return fileName


@app.route("/getvoi",メソッド=['GET'])
getImg() を定義します:
 #ファイル名を取得する ss = request.args['name']
 #返されたレスポンスにファイルを追加する response = make_response(
  送信ファイル

 #ファイルを削除する def sc():
  os.remove(ss)

 #30秒後にファイルを削除します timer = threading.Timer(30, sc)
 タイマー.開始()

 応答を返す

__name__ == '__main__' の場合:
 app.run(ホスト='0.0.0.0'、ポート=8087)

vueコード

デモファイルコード

<テンプレート>
 <div>
 <div
  v-on:dragover="tts"
  v-on:drop="ttrs"
  スタイル="幅: 800px;高さ: 200px;境界線: 1px 黒一色;フォントサイズ: 40px;行の高さ: 200px"
 >
  {{dt}}
 </div>
 <div
  v-for="(item, index) in fileList"
  :key="インデックス"
  スタイル="幅: 800px;高さ: 200px;境界線: 1px 黒一色;フォントサイズ: 40px;位置: 相対;上: 10px"
 >
  <p
  style="font-size: 20px;float: left;position: relative;left: 20pxword-wrap:break-word;word-break:normal;"
  >
  {{アイテム名}}
  </p>
  <h5 style="float:right;position: absolute;top: 80px;right: 20px">
  {{アイテムタイプ}}
  </h5>
  <h6 スタイル="位置: 絶対;上: 80px;フロート: 左;左: 20px">
  {{ item.size | サイズタイプ }}
  </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}} 秒</div>
 </div>
</テンプレート>

<スクリプト>
「axios」からaxiosをインポートします。

エクスポートデフォルト{
 名前: "trs",
 データ() {
 戻る {
  dt: "", // アップロードリマインダー「ファイルをアップロードするにはここにドラッグしてください」または「アップロードが完了しました。アップロードを続行できます」
  fileList: [], // ファイルリストの読み込み: false,
  srcs: "", //画像/ビデオ/オーディオ base64
  isImage: false, //画像ですか? isAudio: false, //音声ですか? isVideo: false, //動画ですか? isCan: true, //合成できますか? isCans: true, //ダウンロードできますか? herfs: "", //ダウンロードアドレス fileName: "", //ファイル名 times: 25 //ダウンロード有効期間 };
 },
 フィルター:
 //ファイルサイズのフォーマット sizeType(val) {
  kbs = val / 1024とします。
  mbs = 0 とします。
  gbs = 0 とします。
  (kbs >= 1024)の場合{
  mbs = kbs / 1024;
  }
  mbs >= 1024の場合{
  gbs = mbs / 1024;
  gbs.toFixed(2) + "GB" を返します。
  } それ以外の場合 (mbs >= 1) {
  mbs.toFixed(2) + "MB"を返します。
  } それ以外 {
  kbs.toFixed(2) + "KB"を返します。
  }
 }
 },
 マウント() {
 vm = this とします。
 window.addEventListener("dragdrop", this.testfunc, false);

 //ページ内にファイルドラッグリマインダーがあるときのグローバル監視ここにドラッグ document.addEventListener("dragover", function() {
  コンソールログ(111);
  vm.dt = "ファイルをアップロードするにはここにドラッグしてください";
  コンソールログ(vm.dt);
 });
 },
 メソッド: {
 //表示ファイルは主に画像/ビデオ/オーディオの3種類です。readFile(file) {
  vm = this とします。
  リーダーを新しいFileReader()にします。
  reader.readAsDataURL(ファイル);
  リーダー.onload = 関数() {
  type = file.type.substr(0, 5); とします。
  if (type == "画像") {
   vm.isImage が true である。
   vm.isAudio = false;
   vm.isVideo = false;
  } そうでない場合 (type == "audio") {
   vm.isImage = false;
   vm.isAudio が true である。
   vm.isVideo = false;
  } そうでない場合 (type == "video") {
   vm.isImage = false;
   vm.isAudio = false;
   vm.isVideo が true である。
  } それ以外 {
   alert("画像/ビデオ/オーディオではありません");
  }
  vm.srcs = リーダーの結果;
  // this.$nextTick(()=>{
  //
  // })
  };
 },
 //ドロップのトリガーイベントをグローバルに監視してドロップポップアップウィンドウの表示をキャンセルするリソース testfunc(event) {
  アラート("ドラッグドロップ!");

  //ドロップポップアップウィンドウの表示リソースをキャンセルします。event.stopPropagation();
  イベントをデフォルトにしない();
 },
 del(インデックス) {
  this.fileList.splice(インデックス、1);
  if (this.fileList.length === 0) {
  this.dt = "";
  }
 },
 //div アップロード ボックスを監視し、ファイルがドラッグされたときに「ファイルをアップロードするにはここにドラッグしてください」と表示します
 tts(e) {
  コンソールログ(e);
  this.dt = "ファイルをアップロードするにはここにドラッグしてください";
 },
 //divアップロードボックスのドロップイベントをリッスンしてttrs(e)をトリガーします{
  コンソールログ(e);
  console.log(e.dataTransfer.files);

  //ファイルを取得する let datas = e.dataTransfer.files;

  //ドロップポップアップウィンドウの表示リソースをキャンセルします e.stopPropagation();
  e.preventDefault();
  datas.forEach(item => {
  if(item.type=="video/mp4"){
   this.fileList.push(アイテム);
  }
  });

  // ファイルを読み取ります。画像/ビデオ/オーディオを表示したくない場合は、this.readFile(this.fileList[this.fileList.length - 1]); を無視できます。



  this.dt = "アップロードが完了しました。アップロードを続行できます";
 },

 //サーバーにファイルをアップロードする ups(){
  if(this.fileList.length==0){
  this.$message('ファイルリストは空です');
  戻る ;
  }
  this.loading = true;
  this.isCan = false;
  this.isCans = true;
  ファイルを this.fileList とします。
  formd = new FormData();
  i = 1 とします。

  //アップロードリストファイルを追加します。forEach(item=>{
  formd.append(i+"",item,item.name)
  私は++;
  })
  formd.append("type",i)
  config={を設定します
  ヘッダー:{"Content-Type":"multipart/form-data"}
  }

  //ファイルアップロードリクエスト axios.post("/qwe",formd,config).then(res=>{
  コンソールログ(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(()=>{
   今回は--;
  },1000)
  this.setCans(タイマー)
  })
 },
 setCans(タイマー){
  タイムアウトを設定します(()=>{
  this.isCans = true
  this.isCan = true
  this.fileName = ""
  クリアインターバル(タイマー)
  これを25回繰り返す
  },25000)
 }
 }
};
</スクリプト>

<スタイル スコープ></style>

設定ファイル

モジュール.エクスポート = {
 開発サーバー: {
 // アセットサブディレクトリ: 'static',
 // アセットパブリックパス: '/',
 プロキシ: {
  "/qwe": {
  ターゲット: "http://127.0.0.1:8087/file",
  変更元: true、
  パス書き換え: {
   "^/qwe": ""
  }
  },
  "/voi": {
  ターゲット: "http://127.0.0.1:8087/getvoi",
  変更元: true、
  パス書き換え: {
   "^/voi": ""
  }
  }
 }
 }
};

これで、vue+flask で動画合成機能 (ドラッグアンドドロップアップロード) を実現する方法についての説明は終わりです。vue 動画合成に関するその他の関連コンテンツについては、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vueはドラッグアンドドロップまたはクリックで写真をアップロードする機能を実装しています
  • Vue2をベースにモバイル画像アップロード、圧縮、ドラッグアンドドロップソート、ドラッグアンドドロップ削除機能を実現
  • Vue3 ベースのフルスクリーン ドラッグ アップロード コンポーネント

<<:  Win10 システムに MySQL8.0.13 をインストールする際の問題と解決策

>>:  MySQL v5.7.18 解凍バージョンのインストール詳細チュートリアル

推薦する

CentOS 6.5 インストール mysql5.7 チュートリアル

1. 新機能MySQL 5.7 はエキサイティングなマイルストーンです。デフォルトの InnoDB ...

MySQLデータベース最適化技術の簡単な紹介

成熟したデータベース アーキテクチャは、最初から高可用性、高スケーラビリティなどの機能を備えて設計さ...

Mysql8.0はソート問題を解決するためにウィンドウ関数を使用する

MySQL ウィンドウ関数の紹介MySQL は MySQL 8.0 以降、ウィンドウ関数をサポートし...

Dockerコンテナが起動直後に終了する問題を解決する

最近、Docker がコンテナの起動時に特定のプロセスを直接実行できるようにする方法を調べていたとこ...

Vue ソースコード学習でレスポンシブ性を実装する方法

目次序文1. レスポンシブシステムの重要な要素1. データの変更を監視する方法2. 依存関係を収集す...

Js における new 演算子の役割の詳細な説明

序文Js は現在最も一般的に使用されているコード操作言語であり、その中でも new 演算子は特によく...

プロフェッショナルなMySQL開発設計仕様とSQL記述仕様

チーム開発のプロセスでは、プロジェクトの安定性、コードの効率性、管理の利便性のために、内部開発および...

WeChat ミニプログラム 宝くじ番号ジェネレーター

この記事では、WeChatアプレットの宝くじ番号ジェネレータの具体的なコードを参考までに紹介します。...

MySQL 文字列分割の例 (区切り文字なしの文字列抽出)

区切り文字なしの文字列抽出質問の要件データベース内のフィールド値:実装効果: 1行のデータを複数行に...

Navicat は CSV データを MySQL にインポートします

この記事では、Navicatを使用してcsvデータをmysqlにインポートする方法を参考までに紹介し...

Win10 での MySQL 8.0.15 のインストールと設定のグラフィック チュートリアル

この記事ではMySQL 8.0.15のインストールと設定方法を参考までに記録します。具体的な内容は以...

JSを使用して簡単な計算機を実装する

JSを使用して、参考用の簡単な計算機を完成させます。具体的な内容は次のとおりです。要件: 入力値は数...

Node.js における path.join() の利点の分析

文字列連結ではなく path.join() メソッドを使用する必要があるのはなぜか疑問に思うかもしれ...

MySQL における主キーが 0 であることと主キーの自己選択制約の関係についての詳しい説明 (詳細)

序文この記事は主にMySQLの主キー0と主キー自己排除制約の関係を紹介し、皆さんの参考と学習のために...