欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

微信开发之JS-SDK + PHP实现录音、上传、语音识别

程序员文章站 2022-07-10 20:58:09
先看效果图:先录音,录音成功之后,把录音追加到列表,点击列表可以播放;录音完成之后上传录音,上传成功再语音识别。 微信官方文档 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 实现流程: 一、 公众号配 ......

先看效果图:先录音,录音成功之后,把录音追加到列表,点击列表可以播放;录音完成之后上传录音,上传成功再语音识别。

微信开发之JS-SDK + PHP实现录音、上传、语音识别微信开发之JS-SDK + PHP实现录音、上传、语音识别微信开发之JS-SDK + PHP实现录音、上传、语音识别

 

微信官方文档 https://developers.weixin.qq.com/doc/offiaccount/oa_web_apps/js-sdk.html

实现流程:

一、 公众号配置

1.js安全域名配置:登陆微信公众平台:公众号设置 -> 功能设置 -> js安全域名,域名写到根域名就行,把下载的txt文件放到域名对应的根目录下

微信开发之JS-SDK + PHP实现录音、上传、语音识别

 

 2.配置ip白名单

微信开发之JS-SDK + PHP实现录音、上传、语音识别

 

 

 

 

二、代码展示

1.前端代码

用到了'startrecord', 'stoprecord', 'playvoice', 'uploadvoice', 'translatevoice'五个接口,先调用 startrecord 开始录音,再调用 stoprecord 停止录音,会返回一个音频的本地id,把录音追加的html录音列表中,方便播放录音,使用 playvoice 播放录音列表中的录音,再使用 uploadvoice 把录音上传到微信服务器上,会返回微信服务器上的serverid(感觉上传录音没有使用到),通过使用本地音频id去识别语音

<!doctype html>
<html>
<head>
    <title>语音识别</title>
<meta http-equiv="x-ua-compatible" content="ie=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script type="text/javascript" src="/static/index/js/jquery.js"></script>
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<link rel="stylesheet" type="text/css" href="/static/index/layui/css/layui.css">

      <style>
          button{
              height: 40px;
              width: 120px;
              margin: 25px;
          }
          #ul{
              margin-top: 15px;
              height: 40px;
              line-height: 40px;
              text-align: center;
              padding: 5px;
          }
          #ul li{
              width: 80%;
          }
          #ullist button{
            width:98%;
            height:40px;
            border-radius:5;
            text-align: center;
            margin: 5px;
        }

      </style>
</head>
<body>
    <div class="container" style="width:100%">
        <div class="row">
            <ul class="list-unstyled" id="ullist">
                
            </ul>
        </div>
        <div id="btn" class="navbar-fixed-bottom" style="user-select:none:align-content:center">
            <center>
                <button id="talk_btn" type="button" class="layui-btn">录 音</button>
                <button id="uploadvoice" type="button" class="layui-btn layui-btn-normal">上传 录音</button><br>
                <button id="translatevoice" type="button" class="layui-btn layui-btn-danger" style="width:90%;">语音 识别</button><br>
            </center>
        </div>
    </div>
    
<script type="text/javascript">

    // 全局变量
    var recordtimer = 300;
    var voice={
                  localid:'',
                  serverid:''
              }
    
    wx.config({
        debug: false,
        appid: '{$signpackage.appid}',
        timestamp: {$signpackage.timestamp},
        noncestr: '{$signpackage.noncestr}',
        signature: '{$signpackage.signature}',
        jsapilist: [
          // 所有要调用的 api 都要加到这个列表中
          'startrecord', 'stoprecord', 'playvoice', 'uploadvoice', 'translatevoice'
        ]
      });
      
      // 在这里调用 api
    wx.ready(function () {
        var start;
        var end;
        
        // 开始录音
        $("#talk_btn").on('touchstart',function (event) {
            // console.log(event)
            event.preventdefault();
            start = new date().gettime();

            // 延迟后录音,避免误操作
            recordtimer = settimeout(function () {

                wx.startrecord({
                    success:function () {
                        // 授权录音
                        localstorage.rainallowrecord = 'true';
                    },
                    cancel:function () {
                        console.log('用户拒绝了录音');
                    }
                });
            },300)
        });
        
        //松手结束录音
        $('#talk_btn').on('touchend', function(event){
            event.preventdefault();
            end = new date().gettime();
            
            if((end - start) < 3000){
                end = 0;
                start = 0;
                alert('录音时间不能少于3秒');
                //小于300ms,不录音
                cleartimeout(recordtimer);
            }else{
                
                var mytime = new date().tolocaletimestring();  //获取当前时间
                
                wx.stoprecord({
                  success: function (res) {
                      
                    voice.localid = res.localid;
                    console.log(voice.localid)
                    var str="<li audioid='"+voice.localid+"'><button class='layui-btn layui-btn-primary'>音频任务"+mytime+"</button></li>";
                    $("#ullist").append(str);//显示到列表
                  },
                  fail: function (res) {
                    alert(json.stringify(res));
                  }
                });
            }
        });
    });
    wx.error(function (res) {
        console.log(res)
    });
    
    //list播放语音
    $("ul").on("click", "li", function() {
        var audioid = $(this).attr("audioid");
        
        wx.playvoice({
            localid: audioid
        });
    })

    // 上传语音
    $("#uploadvoice").click(function(){
        
        //调用微信的上传录音接口把本地录音先上传到微信的服务器
        wx.uploadvoice({
            localid:voice.localid,    // 需要上传的音频的本地id,由stoprecord接口获得
            isshowprogresstips: 1,    // 默认为1,显示进度提示
            success:function(res){
                
                if(res.errmsg == 'uploadvoice:ok'){
                    voice.serverid = res.serverid
                    alert('录音上传成功');
                }else{
                    alert(res.errmsg)
                }
            }
        })
    })
    
    // 语音识别
    $("#translatevoice").click(function(){
        wx.translatevoice({
            localid:voice.localid,    // 需要识别的音频的本地id,由录音相关接口获得
            isshowprogresstips:1,    // 默认为1,显示进度提示
            success:function(res){
                console.log(res)
                if(res.errmsg == "translatevoice:ok"){
                    alert(res.translateresult);    // 语音识别的结果
                }else{
                    alert(res.errmsg)
                }
                
            }
        })
    })

</script>

</body>

</html>

后端代码(php)

wechat.php 此类主要是获取accesstoken和jsapiticket

<?php
namespace app\index\controller;
use think\controller;

/**
 * 微信类
 */
class wechat extends controller
{

    protected  $appid = 'xxxxxxxxxxxxx';
    protected  $appsecret = 'xxxxxxxxxxxxxxxxxx';

    /**
    * 微信服务器配置时 验证token的url
    */
    public function checktoken()
    {
        header("content-type: text/html; charset=utf-8");

        //1.将timestamp,nonce,toke按字典顺序排序
        $timestamp = $_get['timestamp'];
        $nonce = $_get['nonce'];
        $token = 'asd123456zxc';
        $signature = $_get['signature'];
        $array = array($timestamp,$nonce,$token);
        //2.将排序后的三个参数拼接之后用sha1加密
        $tmpstr = implode('',$array);
        $tmpstr = sha1($tmpstr);
        //3.将加密后的字符串与signature进行对比,判断该请求是否来自微信
        if($tmpstr == $signature){
            echo $_get['echostr'];
            exit;
        }
    }

    /**
    * curl请求 
    */
    public function http_curl($url, $type = 'get', $res = 'json', $arr = ''){
      $cl = curl_init();
      curl_setopt($cl, curlopt_url, $url);
      curl_setopt($cl, curlopt_returntransfer, 1);
      curl_setopt($cl, curlopt_ssl_verifypeer, false);
      curl_setopt($cl, curlopt_ssl_verifyhost, false);
      if($type == 'post'){
        curl_setopt($cl, curlopt_post, 1);
        curl_setopt($cl, curlopt_postfields, $arr);
      }
      $output = curl_exec($cl);
      curl_close($cl);
      return json_decode($output, true);
      if($res == 'json'){
        if( curl_error($cl)){
          return curl_error($cl);
        }else{
          return json_decode($output, true);
        }
      }
    }

    /**
     * 获取 accesstoken
     */
    public function getaccesstoken()
    {
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=".$this->appid."&secret=".$this->appsecret;

        // 先判断 access_token 文件里的token是否过期,没过期继续使用,过期就更新
        $data = json_decode($this->get_php_file(root_path."public".ds."wxtxt".ds."access_token.txt"));
        // 过期 更新
        if ($data->expire_time < time()) {
            
            $res = $this->http_curl($url);
            $access_token = $res['access_token'];
            if ($access_token) {
                // 在当前时间戳的基础上加7000s (两小时)
                $data->expire_time = time() + 7000;
                $data->access_token = $res['access_token'];
                $this->set_php_file(root_path."public".ds."wxtxt".ds."access_token.txt",json_encode($data));
            }
        }else{
            // 未过期 直接使用
            $access_token = $data->access_token;
        }
        return $access_token;
    }
    
      /**
     * 获取 jsapiticket
     */
      public function getjsapiticket()
      {
          // 先判断 jsapi_ticket是否过期 没过期继续使用,过期就更新
          $data = json_decode($this->get_php_file(root_path."public".ds."wxtxt".ds."jsapi_ticket.txt"));

          if ($data->expire_time < time()) {
              // 过期 更新
              $accesstoken = $this->getaccesstoken();
              $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token=$accesstoken";
              $res = $this->http_curl($url);
              $ticket = $res['ticket'];
              if ($ticket) {
                  $data->expire_time = time() + 7000;
                  $data->jsapi_ticket = $ticket;
                  $this->set_php_file(root_path."public".ds."wxtxt".ds."jsapi_ticket.txt",json_encode($data));
              }
          }else{
              $ticket = $data->jsapi_ticket;
          }
          return $ticket;
      }


    // 获取存储文件中的token ticket
    private function get_php_file($filename) {
        return trim(file_get_contents($filename));
      }
      // 把token ticket 存储到文件中
      private function set_php_file($filename, $content) {
        $fp = fopen($filename, "w");
        fwrite($fp,  $content);
        fclose($fp);
      }

}

wxmedia.php  此类是返回语音识别的配置信息

<?php
namespace app\index\controller;
use think\controller;
use app\index\controller\wechat;

/**
 * 微信语音识别
 */
class wxmedia extends wechat
{

    /**
     * 语音识别
     */
    public function index()
    {
        $signpackage = json_decode($this->getsignpackage(),true);
    
        $this->assign('signpackage',$signpackage);
        return $this->fetch();
    }

    /**
     * 生成签名
     */
    public function getsignpackage() 
    {

        // 实例化微信操作类
        $wx = new wechat();

        // 获取 ticket
        $jsapiticket = $wx->getjsapiticket();

        // 注意 url 一定要动态获取,不能 hardcode.
        $protocol = (!empty($_server['https']) && $_server['https'] !== 'off' || $_server['server_port'] == 443) ? "https://" : "http://";
        // 当前页面的url
        $url = "$protocol$_server[http_host]$_server[request_uri]";

        $timestamp = time();    //生成签名的时间戳
        $noncestr = $this->createnoncestr();    //生成前面的随机串

        // 这里参数的顺序要按照 key 值 ascii 码升序排序
        $string = "jsapi_ticket=$jsapiticket&noncestr=$noncestr&timestamp=$timestamp&url=$url";
        // 对string进行sha1加密
        $signature = sha1($string);

        $signpackage = array(
          "appid"     => $wx->appid,
          "noncestr"  => $noncestr,
          "timestamp" => $timestamp,
          "url"       => $url,
          "signature" => $signature,
          "rawstring" => $string
        );
        return json_encode($signpackage); 
    }

    /**
     * 生成签名的随机串
     */
    private function createnoncestr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
          $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
    
}

 项目源码: