美文网首页
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