Appearance
实现 音频格式转换 工具
分类:general | 标签:音频、格式转换、ffmpeg 音频在 MP3/WAV/OGG/FLAC 间互转
2.1 功能说明
音频在 MP3/WAV/OGG/FLAC 间互转
核心能力
- 音频文件
- 目标格式
- MP3
- WAV
- OGG
- FLAC
2.1.1 使用指南
在浏览器本地把音频在 MP3 / WAV / OGG / FLAC 之间互转,文件不上传服务器。
- 首次转换会从 CDN 加载 FFmpeg 内核(需联网),之后可离线复用。
- 大文件转换较慢,请耐心等待进度条。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
onFile()ensureFFmpeg()convert()download()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { ref, onBeforeUnmount } from 'vue'
import ToolPageShell from '@/components/ToolPageShell.vue'
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
const file = ref(null)
const fileName = ref('')
const target = ref('mp3')
const error = ref('')
const loading = ref(false)
const busy = ref(false)
const progress = ref(0)
const url = ref('')
let ffmpeg = null
function onFile(e) {
error.value = ''
const f = e.target.files && e.target.files[0]
if (!f) return
if (!f.type.startsWith('audio/') && !/\.(wav|mp3|m4a|ogg|flac|aac|wma)$/i.test(f.name)) {
error.value = '请选择音频文件'
return
}
file.value = f
fileName.value = f.name
}
async function ensureFFmpeg() {
if (ffmpeg) return
loading.value = true
ffmpeg = new FFmpeg()
ffmpeg.on('progress', ({ progress: p }) => {
progress.value = Math.min(99, Math.round(p * 100))
})
const base = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.6/dist/umd'
try {
await ffmpeg.load({
coreURL: await toBlobURL(`${base}/ffmpeg-core.js`, 'text/javascript'),
wasmURL: await toBlobURL(`${base}/ffmpeg-core.wasm`, 'application/wasm')
})
} finally {
loading.value = false
}
}
async function convert() {
error.value = ''
if (!file.value) {
error.value = '请先选择音频文件'
return
}
busy.value = true
progress.value = 0
if (url.value) URL.revokeObjectURL(url.value)
try {
await ensureFFmpeg()
const ext = (file.value.name.split('.').pop() || 'wav').toLowerCase()
const inName = `input.${ext}`
const outName = `output.${target.value}`
await ffmpeg.writeFile(inName, await fetchFile(file.value))
await ffmpeg.exec(['-i', inName, outName])
const data = await ffmpeg.readFile(outName)
const mime = { mp3: 'audio/mpeg', wav: 'audio/wav', ogg: 'audio/ogg', flac: 'audio/flac' }[target.value]
url.value = URL.createObjectURL(new Blob([data.buffer], { type: mime }))
} catch (e) {
error.value = '转换失败:' + (e && e.message ? e.message : String(e))
} finally {
busy.value = false
progress.value = 0
}
}
function download() {
if (!url.value) return
const a = document.createElement('a')
a.href = url.value
a.download = `converted.${target.value}`
a.click()
}
onBeforeUnmount(() => {
if (url.value) URL.revokeObjectURL(url.value)
})2.3 效果截图
