前端压缩视频(@ffmpeg/ffmpeg)和图片(browser-image-compression)
1.压缩图片
安装插件
npm install browser-image-compression --save
引入插件
import { createWorker } from 'browser-image-compression';
定义方法
// 压缩图片
async compressImage(file, maxSizeMB) {
const options = {
maxSizeMB,
maxWidthOrHeight: 1920,
useWebWorker: true
};
try {
const compressedFile = await imageCompression(file, options);
const fileExtension = file.name.split('.').pop();
const compressedFileName = `${Date.now()}.${fileExtension}`;
return new File([compressedFile], compressedFileName, { type: file.type });
} catch (error) {
console.error('图片压缩失败:', error);
return file; // 压缩失败则使用原文件
}
},
2.压缩视频
安装插件
npm install @ffmpeg/[email protected] --save
npm install @ffmpeg/[email protected] --save
npm install @ffmpeg/[email protected] --save
引入插件
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile ,toBlobURL} from '@ffmpeg/util' // 注意来源包变化
定义方法
// 初始化 FFmpeg
async initFFmpeg() {
this.initStatus = 'loading'
try {
this.ffmpeg = new FFmpeg()
// 加载关键资源
await this.ffmpeg.load({
coreURL: '/public/ffmpeg-core.js',
wasmURL: '/public/ffmpeg-core.wasm',
workerURL: '/public/ffmpeg-core.worker.js'
})
// 验证初始化完成
if (!this.ffmpeg.loaded) {
throw new Error('FFmpeg 核心加载失败')
}
console.log('FFmpeg 初始化完成');
this.initStatus = 'ready'
this.retryCount = 0
} catch (err) {
this.initStatus = 'error'
console.error('初始化失败:', err)
}
}
// 压缩视频
async compressVideo() {
if (!this.ffmpeg?.loaded) {
alert('FFmpeg 未初始化完成,请稍后重试')
return
}
try {
await this.ffmpeg.writeFile('input.mp4', await fetchFile(this.currentFile))
await this.executeCompression(10)
return await this.outputResult()
} catch (err) {
console.log(222);
// this.handleError(err)
}
},
// 开始压缩
async executeCompression(maxSizeMB) {
const bitrate = maxSizeMB * 8 * 1024 * 1024 / 10; // 计算目标比特率
const crf = 23; // 常用的 CRF 值,数值越小质量越高
console.log('executeCompression');
try {
await this.ffmpeg.exec([
'-i', 'input.mp4',
'-b:v', `${6000}k`,
'-vf', 'scale=640:-1', // 调整分辨率,
'-preset', 'fast', // 编码速度
'output.mp4'
]);
console.log('Compression completed successfully');
} catch (err) {
console.error('Compression failed:', err);
// 处理错误,例如显示错误消息
this.status = '压缩失败: ' + err.message;
}
},
// 输出压缩结果
async outputResult() {
console.log('outputResult');
const data = await this.ffmpeg.readFile('output.mp4')
const fileExtension = this.currentFile.name.split('.').pop();
const compressedFileName = `${Date.now()}.${fileExtension}`;
return new File([data], compressedFileName, { type: 'video/mp4' });
// const url = URL.createObjectURL(new Blob([data], { type: 'video/mp4' }))
// 处理下载或预览...
},
完整的代码
{{ isProcessing ? '处理中...' : '开始压缩' }}
{{ progress.fileName }}
{{ progress.percentage }}%
{{ status }}
下载压缩文件
本地起node+express服务
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const app = express();
const port = 9080;
// 配置 CORS
app.use(cors());
app.use((req, res, next) => {
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
next();
});
// 配置 multer 存储
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/'); // 上传文件的存储目录
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname)); // 重命名文件,避免文件名冲突
}
});
// 配置文件过滤
const fileFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image/') || file.mimetype.startsWith('video/') || file.mimetype === 'application/gzip') {
cb(null, true); // 接受文件
} else {
cb(null, false); // 拒绝文件
cb(new Error('文件类型不被允许'));
}
};
// 配置 multer
const upload = multer({
storage: storage,
limits: {
fileSize: 1024 * 1024 * 100 // 限制文件大小为 100MB
},
fileFilter: fileFilter
});
// 创建上传目录
if (!fs.existsSync('uploads')) {
fs.mkdirSync('uploads');
}
// 多文件上传接口
app.post('/upload', upload.array('files'), (req, res) => {
if (req.files && req.files.length > 0) {
const files = req.files.map(file => ({
filename: file.filename,
originalname: file.originalname,
mimetype: file.mimetype,
size: file.size
}));
res.status(200).json({
message: '文件上传成功',
data: files
});
} else {
res.status(400).json({ message: '文件上传失败' });
}
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});