【微信公众号H5】vue 公众号H5 使用微信JSAPI 录音 上传 H5长按禁止复制 一条龙服务 (vant-ui)

官方文档必须首当其冲 

1.微信jsAPI 录音文档

2.获取微信临时素材文档

判断是否是微信环境 不是的话跳到 微信提示页面

var ua = navigator.userAgent.toLowerCase();

var isWeixin = ua.indexOf('micromessenger') != -1;

if (!isWeixin) { window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=888" }

首先H5录音功能的话 对于普通H5网上是有很多的方法 插件  但是兼容性很差 特别是对于ios 一开始想的是用H5 做个通用的 但是一圈下来 发现兼容性就很难受  

好在项目是基于微信公众号 放弃使用普通H5的想法 转战使用微信提供的 JSAPI 录音功能 一下子解决了兼容的问题 微信已经帮忙处理完毕 

接下来说一下 注意事项  

在使用微信提供的录音功能前

1.首先的是进入微信公众号后台 公众号设置的功能设置 里填写“JS接口安全域名” 一定要记得 

2.先引入微信JS 简单的 

<script src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>(https://res.wx.qq.com/open/js/jweixin-1.6.0.js

3.注册微信配置 使用wx.config()   将要使用的api 填写到jsApiList里面 要记得 

注意:签名问题 一是服务端算法问题 二是前端当前地址和请求的地址不同 也会出现签名错误  关于签名问题 其他文章也有说明产生的原因

录音功能 不是立即使用 所以 只需检测是否配置环境成功即可 wx.ready()..等回调方法

.js 配置注册微信环境代码示例

export async function wechatSharefund (columnInfo) {
  const data = await wechatConfig(location.href.split('#')[0]) // 返回的微信信息
  console.log(data)
  if (data.code === 0) {
    // 注册
    wx.config({
      debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
      appId: data.data.appId, // 必填,公众号的唯一标识
      timestamp: data.data.timestamp, // 必填,生成签名的时间戳
      nonceStr: data.data.nonceStr, // 必填,生成签名的随机串
      signature: data.data.signature, // 必填,签名
      jsApiList: [
        'updateAppMessageShareData',
        'updateTimelineShareData',
        'startRecord',
        'stopRecord',
        'playVoice',
        'onVoiceRecordEnd',
        'uploadVoice',
        'onVoicePlayEnd',
        'downloadVoice'
      ] // 必填,需要使用的JS接口列表
    })
    // 是否成功
    wx.ready(function(res) {
      console.log([res, 90])
      wx.updateAppMessageShareData({ 
        title: '我是自定义首页!!!!!好友' + store.getters.doctorId,
        desc: '自定义描述', // 分享描述
        link: 'https://doctor.taiori.com/doctor/hospitalIndex?userParam=' + store.getters.doctorId,
        imgUrl: '', // 分享图标
        success: function () {
          // 设置成功
        }
      })
      wx.updateTimelineShareData({
        title: "我是自定义首页!!圈" + store.getters.doctorId,
        link: 'https://doctor.taiori.com/doctor/hospitalIndex?userParam=' + store.getters.doctorId,
        imgUrl: '',
        success: function() {
        }
      });
      
    });
    // 是否支持指定JS接口
    wx.checkJsApi({
      jsApiList: ['startRecord'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
      success: function (res) {
      console.log([res, '114'])
      store.commit('jsApiList', res)
      // 以键值对的形式返回,可用的api值true,不可用为false
      // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
      }
    })
    wx.error(function(res){
      console.log(res)
    })
  }

在使用的地方 引入.j文件

import { wechatSharefund } from '.js'
mounted () {
  wechatSharefund()
}

使用简单的

   touchstart () {
      console.log('开始录音')
      if (this.localId) {
        return
      }
      let that = this
      wx.startRecord({
        success: function(e){
            // 开始录音的代码处理
        },
        cancel: function (e) {
            console.log(e)
        }
      })
    },
    touchend () {
      console.log('结束录音')
      if (this.localId) {
        return
      }
      let that = this
      clearInterval(this.timer)
      wx.stopRecord({
        success: function (res) {
          // 结束后的代码处理
        }
      })
    }

 最后 会涉及到 保存录音 及 上传到自己服务器动作 简单的 使用相对于的API

微信临时素材返回的是speex文件 需要解码成想要的播放MAP3或者wav 前端可直接播放的链接 解码微信有提供方法 

uploadVoice() {
                let that = this
                // 上传到微信为临时素材
                wx.uploadVoice({
                    localId: this.localId, // 需要上传的音频的本地ID,由stopRecord接口获得
                    isShowProgressTips: 1, // 默认为1,显示进度提示
                    success: function (res) {
                        // 获得微信服务端音频ID
                        var serverId = res.serverId; // 返回音频的服务器端ID
                        console.log(res)
                         // 服务端提供接口 传递 音频ID 由服务端处理从微信服务端下载临时素材 存为自己的服务器链接 
                        // http请求方式: GET,https调用 https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID 请求示例(示例为通过curl命令获取多媒体文件) 
                        // curl -I -G "https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"
                        // $.ajax({
                        //     url: 'https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID', // 服务端提供的接口链接
                        //     type: 'GET',
                        //     dataType: "json",
                        //     success: function (data) {
                        //         alert('文件已经保存到自己的服务器');
                        //     },
                        //     error: function (xhr, errorType, error) {
                        //         console.log(error);
                        //     }
                        // });

                    }
                });
            }

示例代码 处理误点击 时长不够  无法监听授权过录音设备问题 和 长按事件 冲突

// 在进入页面时 设置监听 录音自动停止(60秒自动结束录音)
mounted () {
     let that = this
    /* eslint-disable */
    clearInterval(this.timer)
    wx.onVoiceRecordEnd({
      complete: function (res) {
        console.log([res, 105])
        clearInterval(that.timer)
        that.$toast.clear()
        that.loId = res.localId
        if (res.localId) {
          wx.uploadVoice({
            localId: res.localId,
            isShowProgressTips: 1,
            success: function (resSoso) {
              that.localId = resSoso.serverId
              that.loadShow = false
              that.num = parseInt(that.num)
              that.touchstartShow = true
            },
          })
        }
      }
   })
}
// 长按录音
touchstart (e) {
      e.preventDefault() // 阻止长按默认事件
      let that = this
      console.log('开始')
      // 清除当前可能存在的录音
      wx.stopRecord()
      that.loadShow = false
      that.touchstartShow = true
      clearInterval(this.timer)
      that.voiceLoadShow = false
      // window.localStorage.start 判断是否授权过录音设备 未授权过的 即调用wx.startRecord授权并在 成功回调内 立即停止录音 wx.stopRecord() window.localStorage.start = true

      if (window.localStorage.start){
        if (this.localId||this.loadShow) {
          return
        }
        that.touchstartShow = false
        // 获取当前时间 并延迟执行录音事件 防止误点触发录音 在touchend事件中做处理
        that.date = new Date().getTime()
        that.startRecordTimer = setTimeout(() => {
          that.voiceLoadShow = true
          that.loadShow = true
          that.touchstartShow = false
          wx.startRecord({
            success: function(e){
              console.log(e)
              clearInterval(that.timer)
              that.timer = setInterval(() => {
                that.num += 0.1
              }, 100)
            },
            fail: function (e) {
              that.loadShow = false
              that.touchstartShow = false
              clearInterval(this.timer)
              that.$toast.clear()
            }
          })
        }, 200)
      } else {
         wx.startRecord({
          success: function(e){
            wx.stopRecord()
            window.localStorage.start = true
          },
          cancel: function(e){
            that.$toast('拒绝授权录音')
          }
         })
      }
      
    },
// 结束录音
touchend (e) {
      console.log('结束')
      let that = this
      that.touchstartShow = true
      clearInterval(this.timer)
      that.$toast.clear()
      // 获取当前时间跟长按事件及时保存的时间 对比 是否是长按 误点的话不执行录音(也是延迟录音的原因)
      if (new Date().getTime() - that.date < 300) {
        that.date = 0
        that.loadShow = false
        that.touchstartShow = true
        clearTimeout(that.startRecordTimer)
        return
      }
      if (that.num > 1) {
        wx.stopRecord({
          success: function (res) {
            console.log([res, 173])
            that.loId = res.localId
            if (that.num > 0 && res.localId) {
              // 录音结束 获取音频ID 并上传微信临时素材
              wx.uploadVoice({
                localId: res.localId,
                isShowProgressTips: 1,
                success: function (resSoso) {
                  // 获取到微信服务端ID  通过ID传本地服务端 下载临时素材到本地服务器
                  that.localId = resSoso.serverId
                  that.loadShow = false
                  that.num = parseInt(that.num)
                  that.touchstartShow = true
                }
              })
            } else {
              that.loadShow = false
              that.touchstartShow = true
              console.log([that.touchstartShow, 'that.touchstartShow'])
            }
          }
        })
      } else {
        that.loadShow = false
        that.touchstartShow = true
        that.$toast('录音时长过短')
      }
    }

 

 对于H5长按禁止复制弹出菜单 走了挺多歪路 想的就是通过css

 *{ // -webkit-touch-callout:none;  /*系统默认菜单被禁用*/

      // -webkit-user-select:none; /*webkit浏览器*/

      // -khtml-user-select:none; /*早期浏览器*/

      // -moz-user-select:none; /*火狐*/

      // -ms-user-select:none;  /*IE10*/

      // user-select:none;}

但是还是会出现问题 并不能很好的禁止 最后则通过js 事件的拦截 在长按事件 加上以下代码 就一行 完美解决

 e.preventDefault() 阻止事件默认行为

 完整组件代码 可直接引用 使用的是 (vant-ui)可以修改 

<template>
    <div class="container">
        <div class="voice-load" v-if="voiceLoadShow">
          <img src="@/assets/comment/inmej.gif" alt="">
          <div>
            说话中...
          </div>
        </div>
        <div class="input-fixed" :style="inputFixedShow?'bottom:0':'bottom:-300px'">
            <div class="flex-justify-between flex-item-center">
                <div class="xs iconfont" @click="saveClick(false)">&#xe608;</div>
                <div class="input-fixed-btns" @click="saveClick(true)">发送</div>
            </div>
            <div style="color:#ABACAC;padding:16px;padding-bottom:0" v-if="answerName">{{answerName}}</div>
            <div class="flex-align-center" style="font-size:14px;padding: 10px 20px 0">
                <div v-if="audioShowets">
                  <img src="@/assets/icon/icon_audios.png" alt="" style="width: 30px;margin-right:10px" @click="audioShow = !audioShow" v-if="audioShow">
                  <img src="@/assets/icon/icon_keyboard.png" alt="" style="width: 30px;margin-right:10px" @click="audioShow = !audioShow" v-else>
                </div>
                <van-field
                    v-model="name"
                    rows="1"
                    :autosize="autosize"
                    type="textarea"
                    placeholder="请输入..."
                    :border="false"
                    class="textarea"
                    ref="inputs"
                    v-if="audioShow"
                    />
                <div v-else class="audis-btn" :style="localId?'justify-content: flex-end;padding-right:10px':''">
                  <div v-if="localId" class="flex-center">
                    <div class="flex-align-center voice" :style="'padding-left:' + (num*2 > 120 ? 120 : ( num*3 > 12 ? num*3 : 12)) + 'px'" @click="playVoice()">
                      {{num}}''
                      <div class="flex-align-center img-view" style="margin-left:2px;padding-left:3px">
                          <img :src="item.src" alt="" :style="{width:index === 0 ? '5px' : (index === 1 ? '4px' : '3px'), opacity:item.type?'1':'0'}" class="voice-img1" v-for="(item, index) in voiceImgArr" :key="index">
                      </div>
                    </div>
                    <div class="flex-center" @click="localId='';num=0"><img src="@/assets/icon/icon_cler.png" alt="" style="width:20px;margin-left:20px"></div>
                  </div>
                  <button v-else-if="touchstartShow" class="flex-center oll" @touchstart="touchstart">
                    按住 说话
                  </button>
                  <button v-else class="flex-center" @touchend="touchend">
                    <img src="@/assets/icon/icon_lz.png" alt=""  class="opop" style="width:25px">
                  </button>
                </div>
            </div>
        </div>
        <van-overlay :show="inputFixedShow" @click="saveClick(false)"/>
    </div>
</template>
<script>
export default {
  props: {
    show: {
      type: Boolean,
      default: false
    },
    audioShowets: {
      type: Boolean,
      default: false
    },
    answerType: {},
    answerName: {}
  },
  computed: {
  },
  watch: {
    show (n) {
      this.name = ''
      this.inputFixedShow = n
      this.audioShow = true
      this.loadShow = false
      this.touchstartShow = true
      this.localId = ''
      this.num = 0
      clearInterval(this.timer)
    },
    audioShowets (n) {
    },
    audioShow () {
      this.localId = ''
      this.num = 0
      this.loadShow = false
      this.touchstartShow = true
    }
  },
  data () {
    return {
      voiceLoadShow: false, // 判断显示录音loader true显示
      audioShow: true, // 判断输入方式是语音还是文本 true为文本
      inputFixedShow: false, // 显示输入框
      touchstartShow: true, // 是否处于录音 false为在录音或已经触发录音
      loadShow: false, // 是否触发了长按录音
      name: '',
      localId: '',
      autosize: {
        maxHeight: '90'
      },
      num: 0, // 录音的时长
      timer: null, // 录音的定时器
      imgVoiceNum: 0,
      imgTimer: null, // 播放语音的定时器
      playShow: true, // 是否已经在播放语音
      loId: '',
      voiceImgArr: [
        {src: require('../assets/image/icon_s.png'), type: true},
        {src: require('../assets/image/icon_t.png'), type: true},
        {src: require('../assets/image/icon_n.png'), type: true}
      ],
      date: '',
      startRecordTimer: null,
      typeShow: true//  true为触发了长按事件 未触发结束事件
    }
  },
  mounted () {
    let that = this
    /* eslint-disable */
    clearInterval(this.timer)
    wx.onVoiceRecordEnd({
      complete: function (res) {
        clearInterval(that.timer)
        that.voiceLoadShow = false
        that.loId = res.localId
        if (res.localId) {
          wx.uploadVoice({
            localId: res.localId,
            isShowProgressTips: 1,
            success: function (resSoso) {
              that.localId = resSoso.serverId
              that.loadShow = false
              that.num = parseInt(that.num)
              that.touchstartShow = true
            },
          })
        }
      }
    })
  },
  methods: {
    /* eslint-disable */
    saveClick (type) {
      this.inputFixedShow = false
      this.$emit('saveChange', {name: this.name, show: type, localId: this.localId, num: this.num})
      this.name = ''
    },
    touchstartss () {
      wx.startRecord()
    },
    touchstart (e) {
      e.preventDefault()
      let that = this
      that.typeShow = true 
      console.log('开始')
      // 停止前面的录音
      wx.stopRecord()
      that.loadShow = false
      that.touchstartShow = true
      clearInterval(this.timer)
      that.voiceLoadShow = false
      // 判断是否授权过
      if (window.localStorage.start){
        if (this.localId||this.loadShow) {
          return
        }
        that.touchstartShow = false
        that.date = new Date().getTime()
        that.startRecordTimer = setTimeout(() => {
          // 提前显示录制的提示loader·
          that.voiceLoadShow = true
          that.loadShow = true
          that.touchstartShow = false
          if (that.typeShow) {
            wx.startRecord({
              success: function(e){
                clearInterval(that.timer)
                that.timer = setInterval(() => {
                  that.num += 0.1
                }, 100)
              },
              fail: function (e) {
                that.loadShow = false
                that.touchstartShow = true
                clearInterval(this.timer)
                that.voiceLoadShow = false
              }
            })
          } else {
            that.loadShow = false
            that.touchstartShow = true
            that.voiceLoadShow = false
            clearInterval(this.timer)
            wx.stopRecord()
          }
        }, 100)
      } else {
         wx.startRecord({
          success: function(e){
            wx.stopRecord()
            window.localStorage.start = true
          },
          cancel: function(e){
            that.$toast('拒绝授权录音')
          }
         })
      }
      
    },
    touchend (e) {
      console.log('结束')
      let that = this
      that.typeShow = false
      that.touchstartShow = true
      clearInterval(that.timer)
      that.voiceLoadShow = false
      if (new Date().getTime() - that.date < 300) {
        that.date = 0
        that.loadShow = false
        that.touchstartShow = true
        clearTimeout(that.startRecordTimer)
        wx.stopRecord()
        return
      }
      if (that.num > 1) {
        wx.stopRecord({
          success: function (res) {
            that.loId = res.localId
            if (that.num > 0 && res.localId) {
              wx.uploadVoice({
                localId: res.localId,
                isShowProgressTips: 1,
                success: function (resSoso) {
                  that.localId = resSoso.serverId
                  that.loadShow = false
                  that.num = parseInt(that.num)
                  that.touchstartShow = true
                  clearInterval(that.timer)
                }
              })
            } else {
              that.loadShow = false
              that.touchstartShow = true
            }
          }
        })
      } else {
        that.loadShow = false
        that.touchstartShow = true
        that.num = 0
        wx.stopRecord()
        that.$toast('录音时长过短')
      }
    },
    playVoice () {
      let that = this
      console.log('我点额~~~')
      if (that.playShow) {
        wx.playVoice({
          localId: that.loId,
          success: function (resSoso) {
            that.playShow = false
            that.voiceImgChange()
            wx.onVoicePlayEnd({
              success: function (res) {
                clearInterval(that.imgTimer)
                that.voiceImgArr = [
                  {src: require('../assets/image/icon_s.png'), type: true},
                  {src: require('../assets/image/icon_t.png'), type: true},
                  {src: require('../assets/image/icon_n.png'), type: true}
                ]
                that.playShow = true
                that.imgVoiceNum = 0
              }
            })
          }
        })
      } else {
        wx.stopVoice({
          localId: that.loId,
          success: function (resSoso) {
            that.playShow = true
            clearInterval(that.imgTimer)
            that.voiceImgArr = [
              {src: require('../assets/image/icon_s.png'), type: true},
              {src: require('../assets/image/icon_t.png'), type: true},
              {src: require('../assets/image/icon_n.png'), type: true}
            ]
            that.imgVoiceNum = 0
          }
        })
      }
    },
    voiceImgChange () {
      this.imgTimer = setInterval(() => {
        this.imgVoiceNum += 1
        switch (this.imgVoiceNum) {
          case 1:
            this.voiceImgArr = [
              {src: require('../assets/image/icon_s.png'), type: false},
              {src: require('../assets/image/icon_t.png'), type: false},
              {src: require('../assets/image/icon_n.png'), type: true}
            ]
            break
          case 2:
            this.voiceImgArr = [
              {src: require('../assets/image/icon_s.png'), type: false},
              {src: require('../assets/image/icon_t.png'), type: true},
              {src: require('../assets/image/icon_n.png'), type: true}
            ]
            break
          case 3:
            this.voiceImgArr = [
              {src: require('../assets/image/icon_s.png'), type: true},
              {src: require('../assets/image/icon_t.png'), type: true},
              {src: require('../assets/image/icon_n.png'), type: true}
            ]
            this.imgVoiceNum = 0
            break
          default:
            break
        }
      }, 500)
    }
  }
}
</script>
<style scoped lang="scss">
@import '../../static/css/iconfont.css';
@function px2rem($px){
  $rem :37.5;
  @return ($px / $rem) + rem;
}
.container{
    // -webkit-touch-callout:none; /*系统默认菜单被禁用*/
    // -webkit-user-select:none; /*webkit浏览器*/
    // -khtml-user-select:none; /*早期浏览器*/
    // -moz-user-select:none;/*火狐*/
    // -ms-user-select:none; /*IE10*/
    // user-select:none;
    .flex-item-center{
      padding: px2rem(16);
      padding-bottom: px2rem(2);
    }
    .xs{
        font-size: px2rem(14);
        color: #000;
        height:  px2rem(28);
        line-height:  px2rem(28);
        width: px2rem(30);
    }
    .voice-load{
      position: fixed;
      top: 50%;
      left: 50%;
      background: rgba(0,0,0,0.8);
      border-radius: 5px;
      width: 120px;
      height: 120px;
      margin-left: -60px;
      margin-top: -60px;
      text-align: center;
      color: #fff;
      font-size: px2rem(14);
      z-index: 102;
      img{
        width: 80px;
      }
    }
    .input-fixed{
        border-radius: px2rem(20) px2rem(20) 0 0;
        position: fixed;
        bottom: -300px;
        left: 0;
        width: 100%;
        background: #fff;
        z-index: 1001;
        padding-bottom: px2rem(5);
        border-top: 1px solid #f4f4f4;
        transition: all 0.51s;
        padding-bottom: px2rem(20);
        .textarea{
            font-size: px2rem(14);
            border: 1px solid #e6e6e6;
            flex: 1;
        }
    }
   .input-fixed-btns{
        width:  px2rem(65);
        font-size: px2rem(14);
        text-align: center;
        height:  px2rem(28);
        line-height:  px2rem(28);
        background: #0A655A;
        border-radius: 5px;
        color: #fff;
    }
    .audis-btn{
      text-align: center;
      flex: 1;
      height: 44px;
      line-height: 44px;
      display: flex;
      align-items: center;
      justify-content: center;
      border: 1px solid #e6e6e6;
      background: #fff;
      -webkit-touch-callout: none; 
    }
    .voice{
      border-radius: 5px;
      background: #0A655A;
      color: #fff;
      padding: 0 10px;
      padding-left: 20px;
      height: 30px;
    }
    .flex-center{
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .opop{
      animation: audio 1000ms infinite;
    }
    @keyframes audio {
      from {
          opacity: 1.0;
      }
      50% {
          opacity: 0.4;
      }
      to {
          opacity: 1.0;
      }
    }
    button{
      background: #fff;
      border: 1px solid transparent;
      width: 100%;
      height: 100%;
      padding: 0;
      // border: 1px solid #e6e6e6;
      // -webkit-touch-callout:none;  /*系统默认菜单被禁用*/
      // -webkit-user-select:none; /*webkit浏览器*/
      // -khtml-user-select:none; /*早期浏览器*/
      // -moz-user-select:none; /*火狐*/
      // -ms-user-select:none;  /*IE10*/
      // user-select:none;
    }
    input,textarea {
      -webkit-user-select:auto; /*webkit浏览器*/
      outline: none;
    }
    .oll{
      // -webkit-touch-callout: none !important;
      //  -webkit-touch-callout:none;  /*系统默认菜单被禁用*/
      // -webkit-user-select:none; /*webkit浏览器*/
      // -khtml-user-select:none; /*早期浏览器*/
      // -moz-user-select:none; /*火狐*/
      // -ms-user-select:none;  /*IE10*/
      // user-select:none;
    }
}
</style>

 

其实作为H5想要做语音识别,自认为还说有各种弊端得,同时还是微信公众号里面,如果小程序得话,或许会简单一点,但是这里是在公众号里面开发,在这个过程中查阅个各种资料,其实里面得东西都大同小异,但是大多数并不全, 首先微信公众号里面得H5开发语音录入,试过各种方法,由于是java渣渣,页面技术只是了解简单得,因此在使用标签得时候,本身并不能适用于当前得需求,因此最后只能选择了微信自带得录音功能,使用微信提供的接口,(具体得接口使用还是去看微信接口得使用) 由于使用微信得接口得话,首先要上传到它得服务器上面去(虽然也有本地得文件储存id,但是好像并没有用),这里就涉及到了一个serverid,这个值是存在于微信临时素材库得id,后期要用它来获取录音得文件。 通过微信提供得获取临时素材文件得接口,得到了文件(具体方法网上一搜一大堆,后期我也会将完整得代码放进资源里面),但是这里有一个坑得问题,他的格式为.amr得格式,但是最后的目的是转换为文字,因此这里涉及到了格式转换 主要用了现成得技术,也是通过下载资源获取得jar以及实现得方法,将.amr转换为了MP3格式 然后就就是最后一部了,将mp3得音频文件转换为文字,我这里用的是讯飞得技术,当然百度得也可以, 其实整个需求得这个流程已经完成了,但是不得不说里面遇到得坑,由于是渣渣,除了人们总说的,转换为mp3得时候会报N/A得错误,我还遇到了unkowFormat这个错误。 这里只是记录一下自己在查询资料时没有一个符合自己需求得资料,同时也希望以后有这样需求得人,可以将思路捋的更加清楚,也可以进行交流.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值