记录---前端Vue使用ffmpeg压缩视频再上传

🧑‍💻 写在开头

点赞 + 收藏 === 学会🤣🤣🤣

保姆级操作步骤,从我实际运行中的项目中摘取的所有相关代码展示如下:

1.Vue项目中安装插件ffmpeg

1.1 插件版本依赖配置

两个插件的版本 "@ffmpeg/core": "^0.10.0", "@ffmpeg/ffmpeg": "^0.10.1"
package.json 和 package-lock.json 都加入如下ffmpeg的版本配置:

1.2 把ffmpeg安装(下载)到项目依赖目录里

terminal运行命令:

1
npm i
或直接运行命令安装ffmpeg:
1
npm install @ffmpeg/ffmpeg @ffmpeg/core -S
安装后package-lock.json会自动写入如下配置:

1.2.1 报错处理

如果出现安装问题:

 ①先在npm i命令后加--legacy-peer-deps 或者   --force运行

1
npm i --force
②如果上步不行,尝试删除这个安装依赖目录node_modules

 

和package-lock.json文件,重试npm i

请参考:

npm ERR! code ERESOLVEnpm ERR! ERESOLVE could not resolve 报错,版本冲突,最全解决步骤(#^.^#)_npm err! code eresolve npm err! eresolve could not-CSDN博客

1.2.2 镜像过期

安装ffmpeg可能提示镜像证书过期

你使用的镜像地址可能还是这个过期的淘宝镜像:https://registry.npm.taobao.org/

按如下步骤重设镜像地址:

①查看镜像:npm config list

②强制清理镜像缓存:npm cache clean --force

③设置镜像:npm config set registry https://registry.npmmirror.com/(国内推荐淘宝新镜像)

也可:npm config set registry https://registry.npmjs.org/

1.3 把ffmpeg安装到项目里

在项目里的ffmpeg插件目录下找到:

ffmpeg-core.js

ffmpeg-core.wasm

ffmpeg-core-worker.js

 复制到项目代码的public目录里

至此,项目里安装ffmpeg完毕。

接下来就是在代码块里使用ffmpeg

2 项目里引用并封装ffmpeg

在util目录下封装ffmpeg.js以便项目全局引用

 封装的工具通过:

1
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';

把ffmpeg插件引入到项目里使用。

完整ffmpeg.js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { createFFmpeg, fetchFile } from '@ffmpeg/ffmpeg';
let ffmpeg = {};
  
ffmpeg.squeezVideo = async function(file, filename, filetype, width, height, msg) {
    console.log('file', file);
    console.log('filename', filename);
    console.log('filetype', filetype);
    console.log('width', width);
    console.log('height', height);
  
    // 分辨率
    const resolution = `${width}x${height}`;
    // 实例化ffmpeg
    const ffmpegObj = createFFmpeg({
        // ffmpeg路径
        corePath: 'ffmpeg-core.js',
        // 日志
        log: true,
        // 进度
        progress: ({ ratio }) => {
            msg = `完成率: ${(ratio * 100.0).toFixed(1)}%`;
        }
    })
  
    var { name } = file;
    // msg = '正在加载 ffmpeg-core.js'
    // 开始加载
    await ffmpegObj.load();
    // msg = '开始压缩'
    // 把文件加到ffmpeg   写文件
    ffmpegObj.FS('writeFile', name, await fetchFile(file));
    // await ffmpeg.run('-i', name, '-b', '2000000', '-fs', '4194304', '-preset medium', 'superfast', 'output.mp4')
    // 开始压缩视频
    const compressedFileSize = this.computeFileSize(file);
    console.log("After compression,this file size is " + compressedFileSize + " Bytes.");
    await ffmpegObj.run('-i', name, '-b', '2000000', '-crf', '18', '-fs', compressedFileSize, '-s', resolution, 'output.mp4');
    // msg = '压缩完成'
    // 压缩所完成,   读文件  压缩后的文件名称为 output.mp4
    const data = ffmpegObj.FS('readFile', 'output.mp4');
  
    // 转换bolb类型
    const blob = new Blob([data], { type: 'text/plain;charset=utf-8' });
  
    return new Promise((resolve, reject) => {
        const file = new window.File([blob], filename, { type: filetype });
        resolve(file);
    })
}
  
ffmpeg.computeFileSize = function(file) {
    if(!file){
        return '0';
    }
    if(file.size / 1024 / 1024 > 60){
        //30M
        return '31457280';
    }else if(file.size / 1024 / 1024 <= 60 && file.size / 1024 / 1024 > 30){
        return file.size / 2;
    }else{
        return file.size;
    }
}
  
// 获取上传视频的url
ffmpeg.getObjectURL = function(file) {
    let url = null;
    window.URL = window.URL || window.webkitURL;
    if (window.URL) {
        url = window.URL.createObjectURL(file);
    } else {
        url = URL.createObjectURL(file);
    }
    return url;
}
  
// 获取视频的宽高分辨率
ffmpeg.getVideoData = function() {
    return new Promise((resolve, reject) => {
        const videoElement = document.getElementById('video');
        videoElement.addEventListener('loadedmetadata', function () {
            resolve({
                width: this.videoWidth,
                height: this.videoHeight,
                duration: this.duration
            })
        });
    })
}
  
export default ffmpeg;

2.1 ffmpeg压缩参数配置

 

-b:指定视频比特率

-crf:恒定速率因子,控制输出视频质量的参数。

这个参数的取值范围为0~51,其中0为无损模式。数值越大,画质越差,生成的文件却越小。

从主观上讲,18~28是一个合理的范围。18被认为是视觉无损的(从技术角度上看当然还是有损的),它的输出视频质量和输入视频相当。

-fs:压缩到指定大小,单位Byte

-s:分辨率

控制压缩后视频质量的最重要的是后面三个参数:crf、fs、s

3.视频上传元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<template>
    <el-upload
        ref='operationVideoUpload'
        :limit="1"
        list-type='text'
        :class="{disabled:addModelParam.attachments.operationVideo.length>0}"
        :action='actionUrl'
        :on-success="(res,file)=>handleVideoSuccess(res,file,'operationVideo')"
        :before-upload='beforeAvatarUploadVideo'
        :on-remove="(file,fileList)=>handleRemove(file,fileList,'operationVideo')"
        :auto-upload='true'
        :on-exceed="handelFileExceed"
        accept='.mp4,.mov,.wmv,.flv,.mvi,.mkv'>
      <el-button style="position: relative; margin: -5px"><i  class="el-icon-circle-plus-outline" style="color: #66b1ff;">上传附件</i></el-button>
      <br/><br/>
      <p>{{ msg }}</p>
    </el-upload>
    <video id="video" hidden controls object-fill="fill"></video>
  </template>

如果不想要展示压缩视频,可以去掉video标签。

4.上传压缩脚本

把封装的ffmpeg.js导入到页面使用:

import ffmpeg from "@/utils/ffmpeg";

完整js脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<script>
import ffmpeg from "@/utils/ffmpeg";
  
export default {
    data() {
        return {
            msg: '',
            videoWidth: '',
            videoHeight: '',
            duration: '',
            actionUrl: '',
            addModelParam: {
                attachments: {
                    operationVideo: []
                }
            },
        }
    },
    created() {
        this.actionUrl = "你的后端上传文件接口地址URL";
    },
    methods: {
        handleVideoSuccess(res, file, code) {
          this.msg = "已完成视频压缩后上传!";
          file.url = res.data.url;
          file.fileId = res.data.fileId;
          this.addModelParam.attachments[code].push(file.fileId);
        },
  
        handleAvatarSuccess(res, file, code) {
            file.url = res.data.url;
            file.fileId = res.data.fileId;
            this.addModelParam.attachments[code].push(file.fileId);
        },
  
        handleRemove(file, fileList, code) {
            this.addModelParam.attachments[code].splice(this.addModelParam.attachments[code].indexOf(file.fileId),1)
        },
        beforeAvatarUploadVideo(file) {
         const isLarge = file.size / 1024 / 1024 > 30;
         if (isLarge) {
           this.msg = "请稍等,过大的视频正在压缩上传中...";
           //压缩视频
           return this.uploadCompressVideo(file);
         }
        },
        handelFileExceed(){
            this.$message('文件数量超出限制!');
        },
         
        // 上传视频文件压缩后再上传
        uploadCompressVideo(file) {
            if (file) {
                let filename = file.name;
                let filetype = file.type;
  
                const videoUrl = ffmpeg.getObjectURL(file);
                const video = document.getElementById('video');
                video.src = videoUrl;
                return ffmpeg.getVideoData().then((videoObj) => {
                    const {width, height} = videoObj;
                    return ffmpeg.squeezVideo(file, filename, filetype, width, height, this.msg);
                })
            }
        },
},
  
}
</script>

注意异步处理:异步压缩,再上传

可使用new Promise();

5.其他配置:

5.1 vue项目根目录下的vue.config.js里加配置

headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module.exports = {
    publicPath: './',
    devServer: {
        client: {
            overlay: false,
        },
        port: 9002,
        headers: {
            'Cross-Origin-Opener-Policy': 'same-origin',
            'Cross-Origin-Embedder-Policy': 'require-corp'
        }
    },
    transpileDependencies: []
  }

  以免出现如下SharedArrayBuffer的报错:

6.其他实现方案

插件video-conversion.js

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

posted @   林恒  阅读(709)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
欢迎阅读『记录---前端Vue使用ffmpeg压缩视频再上传』
点击右上角即可分享
微信分享提示