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



微信官方文檔 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×tamp=$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、隨機字符串、時間戳、簽名
