下载文件的详细讲解

  1. 音频文件下载的核心实现逻辑
    (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 格式),确保文件能被播放器识别。
相关推荐
Momo__2 天前
TypeScript NoInfer<T>——精准控制泛型推断的工具类型
前端·typescript
退休倒计时3 天前
【每日一题】LeetCode 146. LRU 缓存 TypeScript
算法·leetcode·缓存·typescript
kyriewen4 天前
TypeScript 高级类型:我用 infer 写了一个类型安全的 EventBus,终于搞懂了泛型约束
前端·javascript·typescript
月光刺眼4 天前
Bun + TypeScript 后端入门:从类型约束到 LLM API 调用
后端·typescript
天蓝色的鱼鱼4 天前
Node.js 现在能直接跑 TypeScript 了,tsx 和 ts-node 还需要吗?
前端·typescript·node.js
Oo9204 天前
Bun:下一代 JavaScript/TypeScript 运行时,从入门到实践
typescript·bun
Asize5 天前
Bun + TypeScript 实战:从接口约束到 RESTful 路由设计
后端·typescript·代码规范
大家的林语冰5 天前
超越 TypeScript,Flow 强势回归,语法高仿 TS,功能更丰富,类型更安全!
前端·javascript·typescript
用户484526255825 天前
Bun 入门:Bun.serve 零依赖启动 HTTP 服务
typescript
meilindehuzi_a6 天前
构建基于 RESTful 架构的 TodoList 全栈应用:从前后端理论到 TypeScript/Bun 实战
架构·typescript·restful