前端压缩视频(@ffmpeg/ffmpeg)和图片(browser-image-compression)

前端压缩视频(@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' }))
      // 处理下载或预览...
    },
完整的代码





本地起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}`);
});

你可能感兴趣的:(前端,音视频,ffmpeg,vue.js)