美文网首页
vue aduio标签使用 以及 blob数据流数据处理

vue aduio标签使用 以及 blob数据流数据处理

作者: lazy_tomato | 来源:发表于2020-09-25 11:37 被阅读0次

START

  • 最近遇到需求,需要对音频进行处理,展示,但是还是遇到了很多坑,写个文章记录一下,以后避免踩坑。
  • 本着尽可能描述详细的初衷去编写本文,内容略长,请配合目录食用。
  • 仅以此文,纪念一下自己找BUG的一天时间 ღ( ´・ᴗ・` )

目录

1.业务逻辑梳理

  • 说一下应用场景,由于最近做的是微信服务号的二次开发,所有数据,基本上都是从微信官方哪里获取的(数据包含,文字,图片,音频,视频,图文。这里单方面讨论音频

    • 微信官方提供的接口请求音频数据,是根据凭证access_token以及音频的media_id去请求数据的。

    • 微信官方返回的数据是,二进制数据流

    效果 如下图:

    blob数据.png
  • 和后端朋友讨论了一下,这里处理这个数据有两种方案,简单的总结如下:

    • 后端返回音频URL:后端去请求微信的接口,拿到二进制数据流,保存在我司自己的服务器上,再生成一个URL返回给(前端),前端根据URL去播放
    • 后端返回二进制音频数据流:前端直接请求微信接口,拿到二进制数据流,前端处理二进制数据流(拿到数据后,下载播放都是可以的)。

2.项目环境

  • vue 2
  • element-ui
  • sass

3.URL音频处理

  • 由于公司代码不方便展示,所以写个demo做展示。

    效果如下图:

show.png
  • 话不多说,上代码 (原生的 controls 太丑了,所以我写了一个虚拟的)

    template

    <template>
      <div class="audio_page">
        <h2>前端audio(音频)处理</h2>
    
        <div class="voice_show_item">
          <div class="voice_play">
            <img
              v-if="!isPlay"
              :src="pauseImgUrl"
              alt="暂停中"
              @click="
                onPlayOrPause(
                  'https://res.wx.qq.com/voice/getvoice?mediaid=MzI3ODQwMjQwMV8xMDAwMDAxMzE='
                )
              "
            />
            <img
              v-if="isPlay"
              :src="playImgUrl"
              alt="播放中"
              @click="
                onPlayOrPause(
                  'https://res.wx.qq.com/voice/getvoice?mediaid=MzI3ODQwMjQwMV8xMDAwMDAxMzE='
                )
              "
            />
          </div>
          <div class="voice_info">
            <p>{{ submitContent.name }}</p>
            <!-- <div class="voice_progress"></div> -->
            <el-progress
              :percentage="
                currentTime && duration ? (currentTime / duration) * 100 : 0
              "
              status="success"
              :stroke-width="2"
              :show-text="false"
            ></el-progress>
            <div class="voice_time">
              <span class="current_time">{{ currentTime | formatSecond }}</span>
              <span class="duration">{{ duration | formatSecond }}</span>
            </div>
          </div>
          <audio
            :src="voiceUrl"
            ref="audio"
            @timeupdate="onTimeUpedate"
            @loadedmetadata="onLoadedmetadata"
          ></audio>
        </div>
      </div>
    </template>
    
    

    script

    <script>
    // 格式化音频时间 XXX秒 => 00:00:00
    const realFormatSecond = function realFormatSecond(second) {
      var secondType = typeof second;
      if (secondType === "number" || secondType === "string") {
        second = parseInt(second);
        var hours = Math.floor(second / 3600);
        second = second - hours * 3600;
        var mimute = Math.floor(second / 60);
        second = second - mimute * 60;
        return (
          ("0" + hours).slice(-2) +
          ":" +
          ("0" + mimute).slice(-2) +
          ":" +
          ("0" + second).slice(-2)
        );
      } else {
        return "00:00:00";
      }
    };
    
    export default {
      name: "AudioPage",
      data() {
        return {
          submitContent: {
            name: "lazy_tomato",
          },
          isPlay: false,
          playImgUrl: require("../assets/wx_image/play.gif"),
          pauseImgUrl: require("../assets/wx_image/pause.png"),
          voiceUrl: "",
          currentTime: 0,
          duration: 0,
        };
      },
      filters: {
        // 将整数转换成 时分秒
        formatSecond(second = 0) {
          return realFormatSecond(second);
        },
      },
      methods: {
        // 已选音频播放
        onPlayOrPause(url) {
          this.$refs["audio"].src = url;
          if (this.isPlay) {
            this.$refs["audio"].pause();
            this.isPlay = false;
          } else {
            this.$refs["audio"].play();
            this.isPlay = true;
          }
        },
        // 当前播放时间
        onTimeUpedate(e) {
          this.currentTime = e.target.currentTime;
        },
        // 最大播放时长
        onLoadedmetadata(e) {
          this.duration = e.target.duration;
        },
      },
    };
    </script>
    
    

    style

    <style lang="scss" scoped >
    .voice_show_item {
      display: inline-block;
      width: 350px;
      height: 85px;
      border-radius: 5px;
      border: 1px solid #e0e0e0;
      .voice_play {
        float: left;
        width: 72px;
        height: 85px;
        line-height: 85px;
        img {
          margin: 25px 20px;
          width: 32px;
          height: 32px;
          cursor: pointer;
        }
      }
      .voice_info {
        float: left;
        width: 262px;
        p {
          margin: 18px 0;
          width: 100%;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }
        .voice_progress {
          width: 100%;
          height: 2px;
          background-color: #ebebeb;
        }
        .voice_time {
          width: 100%;
          .currentTime {
            float: left;
          }
          .duration {
            float: right;
          }
        }
      }
    }
    </style>
    

3.1 audio标签

方案一: 可以直接在html中写一个 audio标签 动态赋值src 既可

由于原生的音乐播放很丑,所以模拟做了一个假的播放效果 ,代码如下:

 // 已选音频播放
    onPlayOrPause(url) {
      this.$refs["audio"].src = url;
      if (this.isPlay) {
        this.$refs["audio"].pause();
        this.isPlay = false;
      } else {
        this.$refs["audio"].play();
        this.isPlay = true;
      }
    },

方案二 :可以在播放按钮添加 点击事件 new Aduio() 也是可以的

代码如下

let audio = new Audio();
audio.src = url;

audio.pause()  //暂停
audio.play()   //播放

3.2 音频最大时长获取

html

  <audio
        :src="voiceUrl"
        ref="audio"
        @timeupdate="onTimeUpedate"
        @loadedmetadata="onLoadedmetadata"
      ></audio>

js

  // 最大播放时长
    onLoadedmetadata(e) {
      this.duration = e.target.duration;
    },

3.3 音频当前播放时间

html

  <audio
        :src="voiceUrl"
        ref="audio"
        @timeupdate="onTimeUpedate"
        @loadedmetadata="onLoadedmetadata"
      ></audio>

js

// 当前播放时间
    onTimeUpedate(e) {
      this.currentTime = e.target.currentTime;
    },

3.4 播放进度

  • 使用了element-ui 中的进度条,配合 播放时间/ 音频总时间 => 动态显示播放进度,实现音乐播放进度条
  • 这里要判断 播放时间和音频总时间 是否存在,不然 进度条组件会报错。(/ω\),percentage数值区间0-100,而1/0 => Infinity
    <el-progress
    :percentage="
    currentTime && duration ? (currentTime / duration) * 100 : 0
    "
    status="success"
    :stroke-width="2"
    :show-text="false"
    ></el-progress>

3.5 播放时间格式化

const realFormatSecond = function realFormatSecond(second) {
  var secondType = typeof second;
  if (secondType === "number" || secondType === "string") {
    second = parseInt(second);
    var hours = Math.floor(second / 3600);
    second = second - hours * 3600;
    var mimute = Math.floor(second / 60);
    second = second - mimute * 60;
    return (
      ("0" + hours).slice(-2) +
      ":" +
      ("0" + mimute).slice(-2) +
      ":" +
      ("0" + second).slice(-2)
    );
  } else {
    return "00:00:00";
  }
};


//加个过滤器
  filters: {
    // 将整数转换成 时分秒
    formatSecond(second = 0) {
      return realFormatSecond(second);
    },
  },

4.二进制数据流音频处理

4.1整体代码

template

<template>
  <div>
    <h1>获取音频二进制数据流</h1>
    <el-button @click="up">开始获取音频数据</el-button>
    <audio :src="voiceUrl" controls></audio>
  </div>
</template>

script

<script>
export default {
  data() {
    return {
      voiceUrl: "",
    };
  },
  methods: {
    up() {
      this.$axios.defaults.baseURL = "/api";
      this.$axios({
        url: "/custom/weChat/doTest",
        method: "post",
        data: { media_id: "w5M3TLtyN7W4_4E8bL4F_kscTXc9CeV7HfxmZutPpZ4" },
        responseType: "blob",
      })
        .then((res) => {
          const blob = res.data;

          // 1.获取数据流并下载
          // const reader = new FileReader();
          // reader.readAsDataURL(blob);
          // console.log(reader);
          // reader.onload = (e) => {
          //   const a = document.createElement("a");
          //   a.download = `文件名称.mp3`;
          //   a.href = e.target.result;
          //   document.body.appendChild(a);
          //   a.click();
          //   document.body.removeChild(a);
          // };

          // 2.获取数据流处理成url,直接复制给audio的src去使用
          var filereader = new FileReader();
          filereader.readAsArrayBuffer(blob);
          filereader.onload = (e) => {
            console.log(filereader.result);
            var blob = new Blob([filereader.result]);
            this.voiceUrl = URL.createObjectURL(blob);
          };
        })
        .catch((err) => {
          console.log(err);
        });
    },
  },
};
</script>

4.2 请求需要添加配置

 this.$axios({
       url: "/custom/weChat/doTest",
       method: "post",
       data: { media_id: "w5M3TLtyN7W4_4E8bL4F_kscTXc9CeV7HfxmZutPpZ4" },
       // 设置响应类型为 blob ,不设置,拿到的数据格式会有问题
       responseType: "blob",
     })

4.3 下载 二进制数据流的音频

//  声明一个变量  blob 接收数据流      
    const blob = res.data;

//  1.获取数据流并下载
    const reader = new FileReader();
    reader.readAsDataURL(blob);
    reader.onload = (e) => {
        const a = document.createElement("a");
        //下面设置下载的文件名成
        a.download = `文件名称.mp3`;
        a.href = e.target.result;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    };

readAsDataURL 方法会读取指定的 [Blob]或 [File] 对象。读取操作完成的时候,[readyState] 会变成已完成DONE,并触发 loadend 事件,同时 [result]属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。

4.4 数据转换成可访问的url

//  声明一个变量  blob 接收数据流      
    const blob = res.data;

// 2.获取数据流处理成url,直接复制给audio的src去使用
    var filereader = new FileReader();
    filereader.readAsArrayBuffer(blob);
    filereader.onload = (e) => {
        console.log(filereader.result);
        var blob = new Blob([filereader.result]);
        this.voiceUrl = URL.createObjectURL(blob); 
        // this.voiceUrl 就是转换好的url,直接赋值给音频的src即可
    };

5. 音频相关的问题

END

暂时就写那么多啦,后期再补充。ღ( ´・ᴗ・` )比心

相关文章

网友评论

      本文标题:vue aduio标签使用 以及 blob数据流数据处理

      本文链接:https://www.haomeiwen.com/subject/abxfuktx.html