微信公众号网页 微信小程序 语言聊天功能实现 Vue前端到Java后端

  • 时间:
  • 浏览:
  • 来源:互联网

我要做的功能是 微信公众号网页和微信小程序可以互发语音消息功能

微信对两者都提供了语音功能

对于微信公众号网页录音文件的编码方式为 amr

对于微信小程序则支持 mp3等等

以下是微信小程序支持的格式说明 文档地址 https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.start.html

所以有个坑是需要统一编码! 切不可直接使用amr格式, 因为小程序不支持播放!

如果项目已经使用了amr, 且不能回头了, 那就两种格式都存, 微信公众号使用amr文件, 微信小程序使用mp3文件

还有就是 微信开发者工具 不能正常使用录音功能, 做好了 请拿真机测试讲话!!!

vue上使用amr库地址 https://www.npmjs.com/package/benz-amr-recorder

Java代码 amr转mp3, mp3转amr 示例

        <!-- amr转mp3 用 -->
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-core</artifactId>
            <version>2.4.4</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-native-win64</artifactId>
            <version>2.4.4</version>
        </dependency>
        <dependency>
            <groupId>ws.schild</groupId>
            <artifactId>jave-native-linux64</artifactId>
            <version>2.4.4</version>
        </dependency>
import ws.schild.jave.*;
import java.io.File;

public class AmrUtil {

    /**
     * amr转mp3
     */
    public static void toMp3(String path) {
        to(path, 2);
    }

    /**
     * mp3转amr
     */
    public static void toAmr(String path) {
        to(path, 1);
    }

    /**
     * 转换录音格式
     *
     * @param path 录音文件地址
     * @param type 1mp3转amr 2amr转mp3
     */
    private static void to(String path, int type) {
        // 去掉image/
        path = path.substring("image/".length());
        String os = System.getProperty("os.name").toLowerCase();
        if (os.toLowerCase().startsWith("win")) {
            path = "C:" + File.separator + "wkpower" + File.separator + "imageFiles" + File.separator + path;
        } else if (os.toLowerCase().startsWith("linux")) {
            path = File.separator + "wkpower" + File.separator + "imageFiles" + File.separator + path;
        }

        File source = new File(path);
        File target = null;
        if (type == 1) {
            target = new File(path.replace(".mp3", ".amr"));
        } else if (type == 2) {
            target = new File(path.replace(".amr", ".mp3"));
        }
        AudioAttributes audio = new AudioAttributes();
        audio.setCodec("libmp3lame");
        EncodingAttributes attrs = new EncodingAttributes();
        attrs.setFormat("mp3");
        attrs.setAudioAttributes(audio);
        Encoder encoder = new Encoder();
        try {
            MultimediaObject multimediaObject = new MultimediaObject(source);
            encoder.encode(multimediaObject, target, attrs);
        } catch (IllegalArgumentException | EncoderException e) {
            e.printStackTrace();
        }
    }
}

进入正题

本实例只是贴出关键代码

 

一. 微信公众号网页语音功能开发

官方文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#22

只讲核心功能要点

首先是一个区域(这个区域就是录音按钮), 对这个区域进行监听 手指按下事件, 手指松开事件, 手指挪动事件

在手指按下事件 中做的事情就是弹出提示框

    // 开始录音
    startSoundRecording(event) {
      //页面触点Y坐标
      this.recordTouchY = Number(event.touches[0].pageY);
      this.soundPopTip = "正在录音";
      console.log("开始录音");
      // 开始录音的时候 显示录音弹出框
      this.isShowPopSoundBox = true;
      this.soundTips = "松开 发送";
      this.isCancelSound = false;
      let that = this;
      wx.ready(function () {
        // 记录开始录音时间, 为了计算录音时长
        that.soundStartTime = new Date().getTime();
        wx.startRecord();
      });
    },

在手指挪动事件 手指上滑则显示如下 手指监听上滑代码

    // 移动手指
    moveSoundRecording(event) {
      let touch = event.touches[0];
      //页面触点Y坐标
      let soundTouchY = Number(touch.pageY);
      // 判断手指上滑
      if (this.recordTouchY - 50 > soundTouchY) {
        this.soundPopTip = "取消录音";
        this.isCancelSound = true;
        wx.ready(function () {
          wx.stopRecord();
        });
      } else {
        this.soundPopTip = "正在录音";
        this.isCancelSound = false;
      }
    },

松开手指事件

    endStartSoundRecording() {
      const that = this;
      // 结束录音的时候 隐藏录音弹出框
      this.isShowPopSoundBox = false;
      this.soundTips = "按住说话";
      console.log("停止录音");

      let endTime = new Date().getTime();
      const diffTime = endTime - this.soundStartTime;
      // 录音秒数, 如果是微信小程序则直接提供了获取录音时长方法, 不需要再自行处理
      let recordTime = Math.ceil(diffTime / 1000);
      if (recordTime < 2) {
        Toast.fail('录音时间太短')
        return
      }

      // 如果已经取消发送 则直接返回
      if (this.isCancelSound) {
        console.log("取消发送, 取消上传")
        return
      }
      // 上传语音
      wx.ready(function () {
        wx.stopRecord({
          success: function (res) {
            var localId = res.localId;
            console.log("localId: ", localId);
            wx.uploadVoice({
              localId: localId,
              success(res) {
                // 发送消息
                let obj = {
                  content: "2," + res.serverId + "," + recordTime,
                  // 发送用户id
                  toUserId: that.friend.id,
                  // 发送用户的类型 0 用户1营养师2医生
                  toUserType: that.userType,
                  // 发送的消息类型1文字2图片 3语音
                  type: 3
                }
                console.log('营养师发送语音消息', obj)
                sendWSPush(obj)
              }
            });
          }
        })
      });
    }

结束录音后 获取到的是 微信媒体id, 在后端, 通过媒体id向微信下载文件

Java代码

代码只是示例, 自行转换成你们的代码


    /**
     * 根据媒体id获取微信语音资源
     */
    private String getUrlFromWeixin(String mediaId) {
        JSONObject initAccessToken = JSONObject.parseObject(RedisUtils.get("initAccessToken").toString());
        String accessToken = initAccessToken.getString("access_token");
        final String URL = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={1}&media_id={2}";
        byte[] data = new RestTemplate().getForObject(URL, byte[].class, accessToken, mediaId);

}

 

播放语音

播放就没什么难度 了, 由于我不知道微信其中的坑, 就使用了amr编码的文件, 后面也不想改动了, 就网上查了amr库进行播放

库地址 https://www.npmjs.com/package/benz-amr-recorder

    // 文档 https://www.npmjs.com/package/benz-amr-recorder
    // 播放指定声音
    playSound(item) {
      this.soundInstanceImId = item.imId;
      // 第一次操作
      if (this.isSoundInstancePause === null) {
        this.isSoundInstancePause = false;
      } else {
        this.isSoundInstancePause = !this.isSoundInstancePause;
      }
      // 判断当前播放的类型
      if (this.isSoundInstancePause) {
        console.log("暂停播放 isSoundInstancePause: ", this.isSoundInstancePause);
        // 如果暂停了, 则继续播放
        this.soundInstance.stop();
        // 取消暂停标志
        this.isSoundInstancePause = false;
      } else {
        // 如果没有暂停,则代表第一次播放, 开始初始化对象
        let soundInstance = new BenzAMRRecorder();
        soundInstance.initWithUrl(this.$root.url + '/' + item.content.split(",")[1]).then(() => {
          // 开始播放
          soundInstance.play();
          this.soundInstance = soundInstance;
          console.log("开始播放");
        });
        soundInstance.onEnded(() => {
          // 播放结束, 清空暂停标志
          this.isSoundInstancePause = null;
          console.log("播放结束");
        });
      }
    },

 

 

小程序的录音功能

小程序官方文档 https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/wx.stopRecord.html

定义字段

    // 录音定时器
    soundTimeOutEvent: 0,
    isShowPopSoundBox: false,
    soundTips: "按住 说话",
    soundPopTip: '正在录音',
    soundStartTime: 0,
    // 是否取消发送
    isCancelSound: false,
    // 声音实例对象
    soundInstance: '',
    soundInstanceImId: 0,
    // 当前声音播放状态是否暂停
    isSoundInstancePause: null,
    // 全局的录音管理器
    recorderManager: null,
    // 录音时第一次按住的位置
    recordTouchY: 0

手指按下事件代码

  // 开始录音
  startSoundRecording(event) {
    this.setData({
      soundPopTip: "正在录音",
      // 开始录音的时候 显示录音弹出框
      isShowPopSoundBox: true,
      soundTips: "松开 发送",
      isCancelSound: false,
    })
    console.log("开始录音");
    // 获取录音管理器

    // 获取全局录音管理器, 如果不存在则创建
    if (!this.data.recorderManager) {
      this.setData({
        recorderManager: wx.getRecorderManager()
      })
    }
    let recorderManager = this.data.recorderManager;

    // 开始录音
    recorderManager.start({
      format: 'mp3'
    });

    // 记录第一次按住的位置
    let touch = event.touches[0];
    //页面触点Y坐标
    let soundTouchY = Number(touch.pageY);
    this.setData({
      recordTouchY: soundTouchY
    })
  },

手指移动代码

  // 移动手指
  moveSoundRecording(event) {
    let touch = event.touches[0];
    //页面触点Y坐标
    let soundTouchY = Number(touch.pageY);
    console.log("当前坐标Y: " + soundTouchY + "第一次按住的坐标Y:" + this.data.recordTouchY)
    if (this.data.recordTouchY - 50 > soundTouchY) {
      this.setData({
        soundPopTip: "取消录音",
        isCancelSound: true
      })
    } else {
      this.setData({
        soundPopTip: "正在录音",
        isCancelSound: false
      })
    }
  },

手指松开代码

  endStartSoundRecording() {
    const that = this;
    // 结束录音的时候 隐藏录音弹出框
    this.setData({
      isShowPopSoundBox: false,
      soundTips: "按住说话"
    })
    console.log("停止录音");
    let endTime = new Date().getTime();
    const diffTime = endTime - this.data.soundStartTime;
    // 录音秒数
    let recordTime = Math.ceil(diffTime / 1000);
    if (recordTime < 2) {
      Toast.fail('录音时间太短')
      this.data.recorderManager.stop();
      return
    }
    // 如果已经取消发送 则直接返回
    if (this.data.isCancelSound) {
      console.log("取消发送, 取消上传")
      this.data.recorderManager.stop();
      return
    }
    // 结束录音
    let recorderManager = this.data.recorderManager;
    console.log('recorderManager: ', recorderManager)
    // 监听结束录音事件
    recorderManager.onStop((res) => {
      if (!this.data.isCancelSound) {
        console.log('结束录音', res)
        // 获取到文件地址
        const {
          tempFilePath,
          duration
        } = res
        console.log('tempFilePath', tempFilePath);
        console.log('duration', duration);
        // 上传文件
        wx.uploadFile({
          url: getApp().data.url + '/saveTempImg', //仅为示例,非真实的接口地址
          filePath: tempFilePath,
          name: 'file',
          header: {
            'content-type': 'application/x-www-form-urlencoded'
          },
          success(res) {
            const data = JSON.parse(res.data)
            console.log(data.data)
            let msgobj = {
              content: "1," + data.data + "," + Math.ceil(duration / 1000),
              toUserId: that.data.friend.uuid, // 发送用户id
              toUserType: 0, // 发送用户的类型 0 用户1营养师2医生
              type: 3 // 发送的消息类型1文字0图片
            }
            app.globalData.imService.sendMessage(that.data.friend.uuid, JSON.stringify(msgobj));
          }
        })
      }
    });
    // 结束录音
    recorderManager.stop();
  },

播放语音

  // 播放语音
  playSound(e) {
    console.log("播放语音: ", e)
    this.setData({
      // 当前消息id
      soundInstanceImId: e.currentTarget.dataset.item.imId
    });

    // 维护暂停标志
    if (this.data.isSoundInstancePause === null) {
      this.setData({
        isSoundInstancePause: false
      });
    } else {
      this.setData({
        isSoundInstancePause: !this.data.isSoundInstancePause
      });
    }
    console.log("isSoundInstancePause: ", this.data.isSoundInstancePause)

    // 如果是暂停播放
    if (this.data.isSoundInstancePause) {
      console.log("暂停播放 isSoundInstancePause: ", this.data.isSoundInstancePause);
      // 如果暂停了, 则继续播放
      this.data.soundInstance.stop();
    } else {
      // 如果是播放
      let soundInstance = wx.createInnerAudioContext();
      console.log("开始第一次播放: ", soundInstance);
      this.setData({
        soundInstance: soundInstance
      })
      soundInstance.src = app.data.url + '/' + e.currentTarget.dataset.item.content.split(",")[0];
      console.log(app.data.url + '/' + e.currentTarget.dataset.item.content.split(",")[0])
      // 开始播放
      soundInstance.play();

      // 监听播放完毕事件
      soundInstance.onEnded(() => {
        // 播放结束, 清空暂停标志
        this.setData({
          isSoundInstancePause: null
        })
        console.log("播放结束");
      });
    }
  },

 

本文链接http://www.dzjqx.cn/news/show-617032.html