微信開發之JS-SDK + PHP實現錄音、上傳、語音識別


先看效果圖:先錄音,錄音成功之后,把錄音追加到列表,點擊列表可以播放;錄音完成之后上傳錄音,上傳成功再語音識別。

 

微信官方文檔 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html

實現流程:

一、 公眾號配置

1.JS安全域名配置:登陸微信公眾平台:公眾號設置 -> 功能設置 -> JS安全域名,域名寫到根域名就行,把下載的txt文件放到域名對應的根目錄下

 

 2.配置ip白名單

 

 

 

 

二、代碼展示

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,
          "signature" => $signature
        );
        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;
    }
    
}

思路說明:

1.公眾號配置:JS安全域名、IP白名單

2.獲取AccessToken,需要判斷過期時間

3.獲取JsApiTicket,拿獲取到的AccessToken換取JsApiTicket,需要判斷過期時間

4.生成簽名,需要jsapiTicket+隨機字符串+時間戳+當前URL 組合並sha1加密

5.組合微信JSAPI需要的參數:appId、隨機字符串、時間戳、簽名

 

 項目源碼: https://github.com/zhxiangfei/wechat_voice.git


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM