- 音频文件下载的核心实现逻辑
(1)后端接口适配:GET + Query 参数传递
typescript
// downloadMusic API 定义
export const downloadMusic = (musicId: string, quality: number) => {
return axios({
url: '/api/music/downloadMusic',
method: 'GET',
params: { musicId, quality }, // Query 参数(URL拼接)
responseType: 'blob', // 关键:指定二进制响应类型
headers: { Authorization: localStorage.getItem('auth_token') || '' }, // Token 认证
})
}
- Query 参数:后端改为 Query 传参后,通过 params 字段传递,Axios 会自动拼接为
?musicId=xxx&quality=xxx,避免手动拼接 URL 导致编码错误; - responseType: 'blob':这是文件下载的核心配置 ------ 告诉 Axios 不要把响应解析为 JSON /
字符串,而是保留原始二进制流(Blob 对象),否则音频文件会被破坏。
(2)前端文件下载触发:URL.createObjectURL + a 标签
typescript
// 处理二进制流下载
const blob = response.data // Axios 返回的 Blob 对象
const url = window.URL.createObjectURL(blob) // 生成临时 URL
const link = document.createElement('a')
link.href = url
link.download = `${song.musicName}-${command.quality}.mp3` // 自定义文件名
link.click() // 触发浏览器下载
// 清理临时资源
setTimeout(() => {
document.body.removeChild(link)
window.URL.revokeObjectURL(url) // 释放内存,避免泄漏
}, 100)
- URL.createObjectURL:为 Blob 对象生成浏览器可识别的临时 URL(如
blob:http://localhost:8080/xxx),作为 a 标签的下载地址; - download 属性:指定下载文件的名称和后缀,浏览器会自动触发「保存文件」行为,而非打开文件
- 资源清理:下载完成后必须调用 revokeObjectURL 释放临时 URL,否则会导致内存泄漏(尤其频繁下载时)。
(3)响应类型判断:兼容错误场景
typescript
const contentType = response.headers['content-type'] || response.headers['Content-Type']
if (contentType.includes('application/json')) {
// 后端返回 JSON 错误(如参数错误),解析错误信息
const text = await response.data.text()
const errorData = JSON.parse(text)
ElMessage.error(errorData.errorMsg || '下载失败')
} else {
// 二进制流,执行下载逻辑
}
- 后端可能在接口异常时返回 JSON 错误(而非二进制流),需通过 Content-Type 区分响应类型,避免把错误 JSON
当成音频文件下载。
(4)下载状态控制:防止重复请求
typescript
const downloading = ref(false)
const handleDownload = async (command) => {
if (downloading.value) return // 已有下载请求时,阻止重复触发
downloading.value = true
try {
// 下载逻辑
} catch (error) {
// 错误处理
} finally {
downloading.value = false // 无论成功/失败,重置状态
}
}
- 通过 downloading 状态锁,避免用户短时间内多次点击下载按钮,导致重复请求和资源浪费。
(5)音质映射与文件名处理
typescript
// 音质名称 → 后端识别的数字值
const qualityMap = { 标清: 1, 高清: 2, 无损: 3, 空间音频: 4 }
// 文件名:兼容不同音频格式(MP3/FLAC)
const fileName = `${song.musicName || '未知歌`在这里插入代码片`曲'}-${command.quality}.${contentType.includes('flac') ? 'flac' : 'mp3'}`
- 前端展示「标清 / 高清」等友好名称,后端接收数字编码,通过映射表统一格式;
- 根据响应的 Content-Type 动态设置文件后缀(如无损音质返回 FLAC 格式),确保文件能被播放器识别。