php版微信JS-SDK音频接口的例子
本想这个接口很简单不想写写的,因为看文档它跟图像接口差不多。不过给新用JS-SDK的朋友参照下也好。因为在群中有人说录音后不能转发给用户,经测试是完全可以的,只是时间显示上不对。显示1秒,但不影响播放长度。例子中,我用AJAX提交上传后的media_id,和写固定的open_id 传到后台,调用客服接口发出,注意48小时!这意味着将本地的localId转为serverId后,可以下传回自己服务器,因为微信服务器只保存3天,继续用TP作为框架。
首先是Layout\record.phtml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<link href="__PUBLIC__/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body>
{__CONTENT__}
</body>
<script type="text/javascript">
window.jQuery || document.write("<script src='__PUBLIC__/js/jquery-1.10.2.min.js'>"+"<"+"/script>");
</script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script>
wx.config({
debug: false,
appId: '{$signPackage["appId"]}',
timestamp: {$signPackage["timestamp"]},
nonceStr: '{$signPackage["nonceStr"]}',
signature: '{$signPackage["signature"]}',
jsApiList: [
'checkJsApi',
'startRecord',
'stopRecord',
'onVoiceRecordEnd',
'playVoice',
'pauseVoice',
'stopVoice',
'onVoicePlayEnd',
'uploadVoice',
'downloadVoice',
'getNetworkType'
]
});
wx.ready(function () {
var record = {
localId: '',
serverId: ''
};
wx.getNetworkType({
success: function (res) {
var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi
jQuery(function() {
$('#networkType').html(networkType);
});
}
});
document.querySelector('#record').onclick = function () {
$("#record h2").html('录音中...');
wx.startRecord();
};
document.querySelector('#stoprecord').onclick = function() {
wx.stopRecord({
success: function (res) {
record.localId = res.localId;
$("#record h2").html('开始录音');
}
});
};
wx.onVoiceRecordEnd({
complete: function (res) {
record.localId = res.localId;
$("#record h2").html('开始录音');
}
});
wx.onVoicePlayEnd({
success: function (res) {
//record.localId = res.localId; // 返回音频的本地ID
alert('播放完毕');
}
});
document.querySelector('#playrecord').onclick = function() {
if(record.localId == '') {
alert('你还没录音');
return;
}
wx.playVoice({
localId: record.localId
});
};
document.querySelector('#uploadrecord').onclick = function() {
if(record.localId == '') {
alert('你还没录音localId');
return;
}
wx.uploadVoice({
localId: record.localId,
isShowProgressTips: 1,
success: function (res) {
record.serverId = res.serverId;
$.getJSON('http://www.demo.com/tp/home/index/sendrecord',{openid:'oHdUGj7hZfJfr4ILMB5rgctpNrOw', media_id:record.serverId}, function(data) {
if(data)
alert('发送成功');
});
}
});
};
document.querySelector('#downloadrecord').onclick = function() {
if(record.serverId == '') {
alert('你还没上传录音');
return;
}
wx.downloadVoice({
serverId: record.serverId,
isShowProgressTips: 1,
success: function (res) {
record.localId = res.localId;
alert(record.localId);
}
});
};
});
wx.error(function(res){
var str = res.errMsg;
var reg = /invalid signature$/;
var r = str.match(reg);
if(r !== null) {
jQuery(function(){
$.getJSON('http://www.demo.com/tp/home/index/ticket', function(data) {
if(data) {
alert('ticket update');
location = location;
window.navigate(location);
}
});
});
}
});
</script>
</html>
视图views\Index\record.phtml
<style>
.text-center {text-align: center;}
.btn-hight {height:100px;width:230px;}
#img img{width:200px;}
</style>
<div class="col-lg-12 col-sm-12" style="margin: 12px auto 10px;">
<div class="form-group text-center">
<h2>你使用的网络类型是: <span id="networkType"></span></h2>
</div>
<div class="form-group text-center">
<button id="record" type="button" class="btn btn-primary btn-hight"><h2>开始录音</h2></button>
</div>
<div class="form-group text-center">
<button id="stoprecord" type="button" class="btn btn-warning btn-hight"><h2>停止录音</h2></button>
</div>
<div class="form-group text-center">
<button id="playrecord" type="button" class="btn btn-primary btn-hight"><h2>播放录音</h2></button>
</div>
<div class="form-group text-center">
<button id="pauserecord" type="button" class="btn btn-success btn-hight"><h2>暂停播放</h2></button>
</div>
<div class="form-group text-center">
<button id="uploadrecord" type="button" class="btn btn-danger btn-hight"><h2>上传录音</h2></button>
</div>
<div class="form-group text-center">
<button id="downloadrecord" type="button" class="btn btn-info btn-hight"><h2>下载录音</h2></button>
</div>
</div>
Controller里就加入的方法:
public function recordAction() {
layout('Layout\record');
$this->display();
}
public function sendrecordAction() {
if(IS_AJAX) {
$openid = I('openid','');
$media_id = I('media_id','');
$res = $this->api->call('/message/custom/send', array(
'touser' => $openid,
'msgtype' => 'voice',
'voice' => array('media_id' => $media_id),
), WechatJSAPI::JSON);
if($res)
$this->ajaxReturn(true);
$this->ajaxReturn(false);
}
}
录音效果图(第一次会弹出提示要授权)
上传中效果图:
上传接着发客服消息效果图:
最后在服务号看到的效果图(注意最长60秒,它都只显示1″,但不影响你录制时的长度播放)
根据前面几个地理位置教程,最终把腾讯地图的Web Service API 写完善的类,call不同接口时,注意不同的参数,因为每个差数都稍有不同,是直接影响到显示结果,所以详细请查看官方的文档 Web Service API文档
继续以TP为框架给代码和实例效果!
QQMapModel类:
<?php
namespace Home\Model;
class QQMapModel {
const
SEARCH_API = 'http://apis.map.qq.com/ws/place/v1/search',
SUGGESTION_API = 'http://apis.map.qq.com/ws/place/v1/suggestion',
GEOCODER_API = 'http://apis.map.qq.com/ws/geocoder/v1',
LIST_API = 'http://apis.map.qq.com/ws/district/v1/list',
TRANSLATE_API = 'http://apis.map.qq.com/ws/coord/v1/translate',
PANO_API = 'http://apis.map.qq.com/ws/streetview/v1/getpano';
public
$error_number, $error, $appKey;
protected static
$_instance;
static public function getInstance(array $options = array()) {
if (empty(self::$_instance)) {
self::$_instance = new static($options);
}
return self::$_instance;
}
public function __construct(array $options = array()) {
if(count($options))
$this->appKey = $options['appkey'];
}
public function call($url, array $params = array(), $format_result = true) {
$param = array_merge(array('key' => $this->appKey), $params);
$url = $url.'?'.http_build_query($param);
$ch = curl_init($url);
curl_setopt_array($ch, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_AUTOREFERER => 1,
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_VERBOSE => 1,
));
$res = curl_exec($ch);
$this->error_number = curl_errno($ch);
$this->error = curl_error($ch);
if (curl_errno($ch)) {
return false;
}
curl_close($ch);
return ($format_result ? $this->parseResult($res) : $res);
}
protected function parseResult($res) {
$res = json_decode($res, true);
if ($res['status'] !== 0) {
$this->error_number = $res['status'];
$this->error = $res['message'];
return false;
}
return $res;
}
//取街景图接口
public function streetImage($pano, $otherParam = array()) { //max 960x640
$param = array(
'size' => '620x380',
'pano' => $pano,
'heading' => 0,
'pitch' => 0,
'key' => $this->appKey,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
return 'http://apis.map.qq.com/ws/streetview/v1/image?' . http_build_query($param);
}
//新静态图v2接口
public function staticMap($point, $otherParam = array()) {
$param = array(
'size' => '620*380',
'center' => $point,
'zoom' => 13,
'format' => 'png',
'maptype' => 'roadmap',
'markers' => $point,
'key' => $this->appKey,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
return 'http://apis.map.qq.com/ws/staticmap/v2/?' . http_build_query($param);
}
//旧静态图v1版接口 $point参数 先经度再纬度与上面的相反
public static function mapImage($point, $otherParam = array()) {
$param = array(
'size' => '620*380',
'center' => $point,
'zoom' => 13,
'format' => 'png',
'markers' => $point,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
return 'http://st.map.qq.com/api?' . http_build_query($param);
}
}
ImageCacheModel类新封装一个缓存图片方法:
public static function getApiImg($points, $search, $slug = 'marker') {
$fileName = md5($points);
self::$FULL_CACHE_DIR = C('PUBLIC_FULL_DIR') . self::CACHE_DIR;
$cacheImg = self::$FULL_CACHE_DIR . '/' . $fileName . self::$TYPE;
if (file_exists($cacheImg)) {
return self::CACHE_DIR . $fileName . self::$TYPE;
} else {
$map = QQMapModel::getInstance(array(
'appkey' => 'CZQBZ-RC53V-2RQPX-UFNBE-VDH2J-DFBFJ'
));
$res = $map->call(QQMapModel::SEARCH_API, array(
'keyword' => $search,
'boundary' => "nearby($points,1000)"
));
if ($res) {
$imageUrl = '';
switch($slug) {
case 'marker' :
$label = array();
foreach ($res['data'] as $data) {
//API要求标题不要长过13字符
$label[] = mb_substr($data['title'], 0, 13, 'utf-8') . '|' . $data['location']['lat'] . ',' . $data['location']['lng'];
}
$labels = implode('|', $label);
$labels = 'border:1|size:10|color:brown|bgcolor:orange|' . $labels;
$imageUrl = $map->staticMap($points, array('labels' => $labels, 'zoom' => 15));
break;
case 'label' :
$marker = array();
foreach($res['data'] as $data) {
$lat = $data['location']['lat'];
$lng = $data['location']['lng'];
$marker[] = $lat.','.$lng;
}
$markers = implode('|', $marker);
$markers = 'color:blue|label:H|'.$markers;
$imageUrl = $map->staticMap($points, array('markers' => $markers, 'zoom' => 15));
break;
}
if($imageUrl) {
self::saveCacheImg($imageUrl, $fileName);
return self::CACHE_DIR . $fileName . self::$TYPE;
}
}
}
return self::CACHE_DIR.'default'.self::$TYPE;
}
上面段代码,我主要调用了search接口,并根据参数让生成的地图是图标还是标签文字来显示。
Controller里加入跳转的URL:(因为Layout与之前差不多,这里就不写了)
public function apimapAction() {
layout('Layout/apimap');
$this->display();
}
public function maphotelAction() {
layout(false);
if(I('pos','')) {
$target = ImageCacheModel::getApiImg(I('pos'),'酒店','marker');
$url = __ROOT__.$target;
redirect($url);
}
}
public function mapfoodAction() {
layout(false);
if(I('pos','')) {
$target = ImageCacheModel::getApiImg(I('pos'), '美食', 'label');
$url = __ROOT__.$target;
redirect($url);
}
}
目前Web Service API提供了6个接口,包括根据地址解析或者逆解析,还有坐标转换,我做测试时发现腾讯上的经纬度转成百度后还是编差太大,似乎不同地图API间的经纬度转没什么意义。这些接口就由你们来试了,一通百通~
以酒店查询标签文本的效果图:
无奈之下找微信官方客服,一番令人无语的问答后,最终解决了!原来是API的URL上的/customservice/kfacount/中的kfaccount少了个c,真是操啊!!
以下代码只作参考,毕竟大家所写的方式不同,我的处理过程是,取到表单数据后,先提交给多客服的add接口,成功后,再upload头像,最后再写进数据库!流程处理的代码如下:
/*
* 添加多客服帐号
*/
public function add_customer() {
$data = array();
foreach($_POST['customer'] as $items ) {
if( ! empty($items['value'])) {
$key = $items['name'];
$val = $items['value'];
if($key == 'media_file') {
$img_url = $val;
continue;
}
if($key == 'password') {
$original_pass = $val;
$val = md5($val);
}
$data[$key] = $val;
}
}
$res = $this->call('/kfaccount/add', $data, self::JSON, self::API_TYPE_SERVICE);
if($res) {
if($img_url) {
$this->upload_customer_header($img_url, $data['kf_account']);
$data['local_headimgurl'] = $img_url;
}
global $wpdb;
$table = $wpdb->prefix.'wechat_customer';
if( ! empty($original_pass)) $data['password'] = $original_pass;
$wpdb->insert($table, array_merge($data, array('create_time' => time())));
exit('ok');
}
exit('errcode: '.$this->_error_number.' errmsg: '.$this->_error);
}
要改Wechat-JSON.php API类,新增多客服接口上传方法:
//上传多客服头像 必须是jpg格式,推荐640*640大小的图片以达到最佳效果
public function CustomerHeaderUpload($file_full_path, $account = '') {
$this->_access_token = $this->getAccessToken();
$res = false;
if ($this->_access_token) {
$url = 'https://api.weixin.qq.com/customservice/kfaccount/uploadheadimg?';
$url = $url.'access_token='.$this->_access_token.'&kf_account='.$account;
$res = $this->request($url, array(
'media' => '@'.$file_full_path,
), self::POST);
}
return $res;
}
这是在RFC2397中定义的Data URI scheme,目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入,比如上面那串字符,其实是一张图片,将这些字符复制黏贴到火狐的地址栏中并转到,就能看到了。
在上面的Data URI中,data表示取得数据的协定名称,image/jpeg是数据类型名称,base64是数据的编码方法,逗号后面就是这个image/jpeg文件base64编码后的数据。
目前Data URI scheme支持的类型有:
data:text/plain,文本数据
data:text/html,HTML代码
data:text/html;base64,base64编码的HTML代码
data:text/css,CSS代码
data:text/css;base64,base64编码的CSS代码
data:text/javascript,javascript代码
data:text/javascript;base64,base64编码的Javascript代码
data:image/gif;base64,base64编码的gif图片数据
data:image/png;base64,base64编码的png图片数据
data:image/jpeg;base64,base64编码的jpeg图片数据
data:image/x-icon;base64,base64编码的icon图片数据
$img_file = 'https://img.alicdn.com/bao/uploaded/TB1eaiELpXXXXcPXpXXSutbFXXX.jpg';
$img_info = getimagesize($img_file);
$img_src = "data:{$img_info['mime']};base64," . base64_encode(file_get_contents($img_file));
exit("<img src='{$img_src}' />");
区别:
1、对接口的使用是通过关键字implements。对抽象类的使用是通过关键字extends。当然接口也可以通过关键字extends继承。
2、接口中不可以声明成员变量(包括类静态变量),但是可以声明类常量。抽象类中可以声明各种类型成员变量,实现数据的封装。(另JAVA接口中的成员变量都要声明为public static final类型)
3、接口没有构造函数,抽象类可以有构造函数。
4、接口中的方法默认都是public类型的,而抽象类中的方法可以使用private,protected,public来修饰。
5、一个类可以同时实现多个接口,但一个类只能继承于一个抽象类。
抽象类还是接口。
如果要创建一个模型,这个模型将由一些紧密相关的对象采用,就可以使用抽象类。如果要创建将由一些不相关对象采用的功能,就使用接口。
如果必须从多个来源继承行为,就使用接口。
如果知道所有类都会共享一个公共的行为实现,就使用抽象类,并在其中实现该行为
例子
<?php
abstract class Father {
function meth1() {
echo "meth1...<br>";
}
abstract function meth2();
public $var1="var1";
public static $var2="var2";
const Var3="Var3";
}
class Son extends Father {
function meth2() {
echo "meth2 of Son...<br>";
}
}
$s=new Son();
echo $s->var1."<br>";
echo Father::$var2."<br>";
echo Father::Var3."<br>";
Interface IFather {
//public $iVar1="iVar1"; 此处接口定义中不能包含成员变量
//public static $iVar2="iVar2"; 此处接口定义中不能包含静态变量
const iVar3="iVar3";
function iMeth1();
}
Class ISon implements IFather {
function iMeth1() {
echo "iMeth1...<br>";
}
}
$is=new ISon();
echo IFather::iVar3;
?>
如果更详细一点我们可以对每一个都介绍一下
抽象类:是基于类来说,其本身就是类,只是一种特殊的类,不能直接实例,可以在类里定义方法,属性。类似于模版,规范后让子类实现详细功能。
接口:主要基于方法的规范,有点像抽象类里的抽象方法,只是其相对于抽象方法来说,更加独立。可让某个类通过组合多个方法来形成新的类。
抽象类与接口的相同点:
1、都是用于声明某一种事物,规范名称、参数,形成模块,未有详细的实现细节。
2、都是通过类来实现相关的细节工作
3、语法上,抽象类的抽象方法与接口一样,不能有方法体,即{}符号
4、都可以用继承,接口可以继承接口形成新的接口,抽象类可以继承抽象类从而形成新的抽象类
抽象类与接口的不同点:
1、抽象类可以有属性、普通方法、抽象方法,但接口不能有属性、普通方法、可以有常量
2、抽象类内未必有抽象方法,但接口内一定会有“抽象”方法
3、语法上有不同
4、抽象类用abstract关键字在类前声明,且有class声明为类,接口是用interface来声明,但不能用class来声明,因为接口不是类。
5、抽象类的抽象方法一定要用abstract来声明,而接口则不需要
6、抽象类是用extends关键字让子类继承父类后,在子类实现详细的抽象方法。而接口则是用implements让普通类在类里实现接口的详细方法,且接口可以一次性实现多个方法,用逗号分开各个接口就可
各自的特点:
抽象类内未必有抽象方法,但有抽象方法的类,则必是抽象类
抽象类内,即便全是具体方法,也不能够实例化,只要新建类来继承后,实例继承类才可以
接口可以让一个类一次性实现多个不同的方法
接口本身就是抽象的,但注意不是抽象类,因为接口不是类,只是其方法是抽象的。所以,其也是抽象的
应用与结合:
以下的代码是基于自己的思考,未在实际开发中应用,只是这种写法有点奇特。让抽象与接口结合起来。
一、抽象类与接口的结合
<?php
/*
写此程序源于自己的猜测,想在抽象类里实现某一接口。
*/
interface work{
public function say();
}
abstract class a implements work{
public function showlove(){
echo 'love you<br />';
}
}
class b extends a{
public function say(){
echo 'hello, i m in b';
}
}
$k=new b();
$k->say();
/*
以上程序能正常执行
普通类implements接口后,就变成了抽象类了,这就好像是直接给抽象类增加了一个抽象方法。
*/
二、接口与继承的结合
父类是普通类,子类继承后,同时在子类里实现接口。
疑问:这样的做法是否有意义,在实际开发中是否有这样的应用?
<?php
interface kk{
public function say();
}
class a {
public function show(){
echo '我是父类<br />';
}
}
class b extends a implements kk{
public function say(){
echo '我是继承A类,同时实现say接口的<br />';
}
}
$b=new b();
$b->show();//我是父类
$b->say();//我是继承A类,同时实现say接口的
相关文章
- 这篇文章主要介绍了c# 三种方法调用WebService接口的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
- 这篇文章主要介绍了vue接口请求加密实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
- 这篇文章主要为大家详细介绍了C#微信开发之发送模板消息的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 这篇文章主要介绍了iOS新版微信底部返回横条问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 为公司系统业务需要,这几天了解了一下微信和支付宝扫码支付的接口,并用c#实现了微信和支付宝扫码支付的功能。需要的朋友跟随小编一起看看吧...2020-06-25
- 这篇文章主要介绍了Python爬取微信小程序通用方法代码实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-29
- 这篇文章主要介绍了C#实现的微信网页授权操作逻辑封装,分析了微信网页授权操作的原理、步骤并给出了C#实现的网页授权操作逻辑封装类,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了SpringBoot接口接收json参数解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-19
- 这篇文章主要介绍了C#简单了解接口(Interface)使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-12-08
- 有很多人在做微信的扫一扫下载。但是在微信更新之后微信将该功能给禁止掉了,也不能说是全面禁止吧,因为腾讯、微信是一家嘛,通过应用宝审核的应用好像还是可以通过扫一扫直接下载的,下面通过本篇文章给大家介绍微信扫一扫下载app的代码片段,感兴趣的朋友一起看看吧...2016-01-02
- 这篇文章主要介绍了游戏开发中如何使用CocosCreator进行音效处理,并对音效组件进行封装,方便以后使用,同学们看完之后,一定要亲手实验一下...2021-04-15
- 这篇文章主要介绍了iOS新版微信底部工具栏遮挡问题完美解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 这篇文章主要介绍了Feign接口方法返回值设置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-07-08
- 这篇文章主要介绍了C# Rx的主要接口深入理解的相关资料,需要的朋友可以参考下...2020-06-25
- 这篇文章主要为大家详细介绍了C#图像识别,微信跳一跳机器人,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 这篇文章主要介绍了vue设置全局访问接口API地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-14
华为手机蓝牙可以连接几个设备?华为设置蓝牙音频连接个数的技巧
华为手机蓝牙可以连接几个设备? 华为手机使用蓝牙的时候,想要知道能连接几个设备,这都是可以设置呢?下面我们就来看看华为设置蓝牙音频连接个数的技巧,需要的朋友可以参考下...2020-12-08- 在日常开发中,总会接触到各种接口,前后端数据传输接口,第三方业务平台接口,下面这篇文章主要给大家介绍了关于如何设计一个安全的API接口的相关资料,需要的朋友可以参考下...2021-08-12
- php怎么写api接口?本文介绍了php写api接口的实例代码,有兴趣的同学可以参考一下。 http://localhost/openUser.php?act=get_user_list&type=json在这里openUser.php...2017-07-06
- 这篇文章主要介绍了HTML5实现微信拍摄上传照片功能,实现HTML5 Canvas手机拍摄,本地压缩上传图片时遇到问题的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-04-27