Vue项目中播放/下载文件流音频

6,894 阅读2分钟

前言

最近有个需求,前端搜索音频id,后端返回对应的音频。我在之前的音视频播放开发都是通过前端播放后端返回的音视频资源在服务器上的资源地址,前端赋值给src播放实现的。
这样就会存在一个安全性问题,产品的需求是不想让用户下载音频,所以后端返回二进制文件流给前端在线播放。(本文侧重前端对文件流处理,关于audio标签自定义样式可参考HTML中给audio自定义样式)

怎么做到的?

  1. 前端请求后端接口,后端返回文件流。(重点:注意axios请求时 responseType 要设置为blob或者ArrayBuffer)。
  2. 通过new Blob()方法将后端返回的文件流转为blob对象。
  3. 通过URL.createObjectURL将blob对象转为blob url,将url绑定到audio的src即可以实现音频播放。 前端大致代码如下:
// api.js
loadAudio: params => { // 获取录音
    return axios.post(`${base_url}/audio/loadAudio`, params, {
      responseType: 'blob'
    }).then(res => res.data)
 }
// template 
 <audio :src="audioSrc" controls="controls" id="robot-audio-listen-id">
  您的浏览器不支持 audio 标签。
</audio>
getAudio () {
  var params = new URLSearchParams();
  params.append('name', this.originalAudioItem.name);
  params.append('path', this.originalAudioItem.labelPath);
  api
    .downAudio(params)
    .then(res => {
      // res 返回blob对象,将文件流转为json处理音频获取失败情况
      this.fileToJson(res).then(result => {
        if (result.code === 1) { // 获取音频失败
          this.loading = false;
          this.$message.error(result.msg);
        } else {
          this.handleFileStream(res)
        }
      })
    })
    .catch(error => {
      console.log('error', error);
    });
},
/**
 * 将file对象(.json文件)转为json对象
 * @param {File} file file对象
 */
fileToJson (file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = res => {
      const { result } = res.target // 得到字符串
      const data = JSON.parse(result) // 解析成json对象
      resolve(data)
    } // 成功回调
    reader.onerror = err => {
      reject(err)
    } // 失败回调
    reader.readAsText(new Blob([file]), 'utf-8') // 按照utf-8编码解析
  })
},
// 处理后台返回的文件流并下载
handleFileStream (fileStream) {
  var blod = new Blob([fileStream]);
  this.audioSrc = URL.createObjectURL(blod);
  console.log('audioSrc', this.audioSrc)
}

使用post方式实现下载文件

// utils.js
/**
 * post下载文件
 * @param data {Binary file stream} 二进制文件
 * @param type {Sting} MIME类型
 * @param name {Sting} 文件名
 * @return void
 */
function download(url, name) {
  const aLink = document.createElement('a')
  aLink.download = name
  aLink.href = url
  // aLink.dispatchEvent(new MouseEvent('click', {}))
  document.body.appendChild(aLink)
  aLink.click()
  document.body.removeChild(aLink)
  URL.revokeObjectURL(url) //释放url对象
}

export function downloadFile(data, name, type) {
  const blob = new Blob([data], { type })
  const url = URL.createObjectURL(blob)
  download(url, name)
}
// api.js
modelCompressDown: params => { // 文件下载
    return axios.post(`${base}/afp/model_download_model`, params, {
      responseType: 'blob'
    }).then(res => res.data) 
},
// 点击下载调用此method
 submitDownLoad(path) {
  const pathArr = path.split('/')
  const fileName = pathArr[pathArr.length - 1]
  var params = new URLSearchParams()
  params.append('tenantId', this.userID)
  params.append('fileAddr', path)
  api.fileDown(params).then(res => {
    fileToJson(res).then(result => {
      downloadFile(res, fileName, 'application/x-gzip')
    })
  })
},

参考:
ArrayBuffer(二进制数据的原始缓冲区)

深入理解xhr的responseType中blob和arrayBuffer

JSON对象与Blob对象互相转换

详解FormData 、Blob、File、FileReader、ArrayBuffer、URL、URLSearchParams对象

HTTP协议-MIME类型