php版微信JS-SDK图片接口例子
微信JSSDK的图片接口,能让我们轻松实现上传功能。继续做例子,我们先更新下wx.config的jsApiList数组加入四个’chooseImage’, ‘uploadImage’, ‘downloadImage’, ‘previewImage’。
继续用thinkphp做框架,首先们们新建Layout/image.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',
'chooseImage',
'uploadImage',
'downloadImage',
'previewImage'
]
});
wx.ready(function () {
var images = {
localId: [],
serverId: [],
downloadId: []
};
document.querySelector('#selectImage').onclick = function () {
wx.chooseImage({
success: function (res) {
images.localId = res.localIds;
jQuery(function(){
$.each( res.localIds, function(i, n){
$("#img").append('<img src="'+n+'" /> <br />');
});
});
}
});
};
document.querySelector('#uploadImage').onclick = function () {
if (images.localId.length == 0) {
alert('请先使用选择图片按钮');
return;
}
images.serverId = [];
jQuery(function(){
$.each(images.localId, function(i,n) {
wx.uploadImage({
localId: n,
success: function (res) {
images.serverId.push(res.serverId);
},
fail: function (res) {
alert(JSON.stringify(res));
}
});
});
});
};
document.querySelector('#downloadImage').onclick = function () {
if (images.serverId.length == 0) {
alert('请先按上传图片按钮');
return;
}
jQuery(function() {
$.each(images.serverId, function (i, n) {
wx.downloadImage({
serverId: n,
success: function (res) {
images.downloadId.push(res.localId);
}
});
});
$.each( images.downloadId, function(i, n){
$("#img2").append('<img src="'+n+'" /> <br />');
});
});
};
document.querySelector('#previewImage').onclick = function () {
var imgList = [
'__PUBLIC__/images/gallery/image-1.jpg',
'__PUBLIC__/images/gallery/image-2.jpg'
];
wx.previewImage({
current: imgList[0],
urls: imgList
});
};
});
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/image.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">
<button id="selectImage" type="button" class="btn btn-primary btn-hight"><h2>选择图片</h2></button>
</div>
<div class="form-group text-center">
<div id="img"></div>
</div>
<div class="form-group text-center">
<button id="uploadImage" type="button" class="btn btn-primary btn-hight"><h2>上传图片</h2></button>
</div>
<div class="form-group text-center">
<button id="downloadImage" type="button" class="btn btn-primary btn-hight"><h2>下载图片</h2></button>
</div>
<div class="form-group text-center">
<div id="img2"></div>
</div>
<div class="form-group text-center">
<button id="previewImage" type="button" class="btn btn-primary btn-hight"><h2>预览图片</h2></button>
</div>
</div>
Controller就加入一个imageAction:
public function imageAction() {
layout('Layout/image');
$this->display();
}
选择图片接口效果图:
上传和下载接口的效果图:(注 这里是先上传接口取到media_id再调下载图片接口显示出来,请看layout的js代码)
预览图片接口效果图:
这个支付类是根据官方的文档修改而来!主要实现生成JS API 、Native的package签名包和Native响应的XML格式数据。注释都标上了各方法的用意。由于package包签名,略复杂,这个要自己多花时间去对应去看和log出文件来一一对比!当然只要你用上教程的类,设置好对应的参数就可以正确的生成package参数等
<?php
if (isset($argc) && $argc >= 1 && $argv[0] == __FILE__) {
//初始化pay的必要信息
$pay = new WechatPay(array(
WechatPay::APPID => 'wx99dabzpiuq83985b8',
WechatPay::APPSERCER => 'ac12e7e4abaer63hkoa0cc36a9663fa',
WechatPay::PARTNERKEY => 'bae4sfa3562rsfaq23s2045',
WechatPay::PARTNERID => '1268969802',
WechatPay::PAYSIGNKEY => '9Fqsxb3PK4IVOCEc4yCquy5zecS9LeeMjF2Nn4B4YKoOxPwaQdFwMezKT8oNlBYaWcuT',
WechatPay::SIGNTYPE => 'sha1',
));
//设置package 必要的参数 jsapi native都通用
$pay->setParams(WechatPay::BANK_TYPE, "WX");
$pay->setParams(WechatPay::BODY, "test");
$pay->setParams(WechatPay::PARTNER, $pay->partnerid);
$pay->setParams(WechatPay::OUT_TRADE_NO, commonUtil::createNoncestr());
$pay->setParams(WechatPay::TOTAL_FEE, "1");
$pay->setParams(WechatPay::FEE_TYPE, "1");
$pay->setParams(WechatPay::TIMESTAMP, time());
$pay->setParams(WechatPay::NOTIFY_URL, "http://www.demo.com/notify");
$pay->setParams(WechatPay::SPBILL_CREATE_IP, "127.0.0.1");
$pay->setParams(WechatPay::INPUT_CHARSET, "UTF-8");
//JSAPI的签名json
print_r($pay->createJsApiPackage());
//生成native XML
print_r($pay->createNativePackage());
//生成native URL
print_r($pay->createNativeUrl("9701"));
}
JS API生成的package签名包参数:
{
"appId":"wx9998ff5f4dede5b7",
"package":"bank_type=WX&body=test&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fwww.demo.com%2Fnotify&out_trade_no=Vf5qsSwtu0hc2loH&partner=wx9998ff5f4dede5b7&spbill_create_ip=127.0.0.1×tamp=1409295711&total_fee=1&sign=FEE0167BD0D89A88BF6850590EA889B6",
"timeStamp":1409295711,
"nonceStr":"Vf5qsSwtu0hc2loH",
"paySign":"f816264c750923863c370a1739640244b0c2d39c",
"signType":"sha1"
}
Native 响应的XML格式:
<xml>
<AppId><![CDATA[wx9998ff5f4dede5b7]]></AppId>
<Package>
<![CDATA[bank_type=WX&body=test&fee_type=1&input_charset=UTF-8¬ify_url=http%3A%2F%2Fwww.demo.com%2Fnotify&out_trade_no=GDl3what4sALDEAd&partner=wx9998ff5f4dede5b7&spbill_create_ip=127.0.0.1×tamp=1409296124&total_fee=1&sign=BF949B85570644B939B369FD44B0C4A9]]>
</Package>
<TimeStamp>1409296124</TimeStamp>
<NonceStr><![CDATA[GDl3what4sALDEAd]]></NonceStr>
<RetCode>0</RetCode>
<RetErrMsg><![CDATA[ok]]></RetErrMsg>
<AppSignature><![CDATA[ca4a2467b817a62c38a9801fcf451f51692027bf]]></AppSignature>
<SignMethod><![CDATA[sha1]]></SignMethod>
</xml>
Native的URL链接:
weixin://wxpay/bizpayurl?appid=wx9998ff5f4dede5b7&noncestr=VY7cVA6mtVrc1BVq&productid=9701&sign=43508b65b50e1d7e1089be66d55a709469155d73×tamp=1409296323
无论哪一种方式,我们都要通过setParams来设置必要初始化参数和商品价格和状态等!
WechatPay class:
<?php
class WechatPay {
const
BANK_TYPE = 'bank_type',
BODY = 'body',
PARTNER = 'partner',
OUT_TRADE_NO = 'out_trade_no',
TIMESTAMP = 'timestamp',
TOTAL_FEE = 'total_fee',
FEE_TYPE = 'fee_type',
NOTIFY_URL = 'notify_url',
SPBILL_CREATE_IP = 'spbill_create_ip',
INPUT_CHARSET = 'input_charset',
APPID = 'appid',
APPSERCER = 'appsercer',
PAYSIGNKEY = 'appkey',
PARTNERID = 'partnerid',
PARTNERKEY = 'partnerkey',
SIGNTYPE = 'signtype';
public
$params = array(), $partnerid = '';
static protected
$_instance;
protected
$_appid, $_appkey, $_signtype, $_partnerkey, $_appsercer;
static public function getInstance(array $options = array()) {
if (empty(self::$_instance)) {
self::$_instance = new self ($options);
}
return self::$_instance;
}
public function __construct(array $options = array()){
$this->_appid = $options[self::APPID];
$this->_appkey = $options[self::PAYSIGNKEY];
$this->_signtype = $options[self::SIGNTYPE];
$this->_partnerkey = $options[self::PARTNERKEY];
$this->_appsercer = $options[self::APPSERCER];
$this->partnerid = $options[self::APPID];
}
public function setParams($param, $paramValue) {
$this->params[CommonUtil::trimString($param)] = CommonUtil::trimString($paramValue);
}
public function getParams($param) {
return $this->params[$param];
}
protected 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;
}
public function checkParams(){
//必要的9个参与签名的参数
if($this->params[self::BANK_TYPE] == null || $this->params[self::BODY] == null || $this->params[self::PARTNER] == null ||
$this->params[self::OUT_TRADE_NO] == null || $this->params[self::TOTAL_FEE] == null || $this->params[self::FEE_TYPE] == null ||
$this->params[self::NOTIFY_URL] == null || $this->params[self::SPBILL_CREATE_IP] == null || $this->params[self::INPUT_CHARSET] == null
) {
return false;
}
return true;
}
/*
* 生成package包
* @params 初始化类时用setParams方法定义必要的9个参数
* 排序后格式化url query形式 再md5SignUtil类签名,再给合URL
*/
protected function getPackageSign(){
try {
if (null == $this->_partnerkey || "" == $this->_partnerkey ) {
throw new Exception("密钥不能为空!" . "<br>");
}
$commonUtil = new CommonUtil();
ksort($this->params);
$unSignParaString = $commonUtil->formatUrlQuery($this->params, false);
$paraString = $commonUtil->formatUrlQuery($this->params, true);
$md5SignUtil = new MD5SignUtil();
return $paraString . "&sign=" . $md5SignUtil->sign($unSignParaString,commonUtil::trimString($this->_partnerkey));
} catch (Exception $e) {
echo ($e->getMessage());
}
}
/*
* 生成签名方法
* @params appid appkey package timestamp noncestr 等参数而native事例代码中加上retcode reterrmsg两个参数
*/
public function getPaySign($signObj){
foreach ($signObj as $k => $v){
$signParams[strtolower($k)] = $v;
}
try {
if ($this->_appkey == "") {
throw new Exception("APPKEY为空!" . "<br>");
}
$signParams["appkey"] = $this->_appkey;
ksort($signParams, SORT_STRING);
$commonUtil = new CommonUtil();
$signString = $commonUtil->formatPayUrlQuery($signParams, false);
return sha1($signString);
} catch (Exception $e) {
echo ($e->getMessage());
}
}
//JS API 签名 其中nonceStr是作为订单号 灌穿整个支付流程
public function createJsApiPackage(){
try {
if($this->checkParams() == false) {
throw new Exception("生成package参数缺失!" . "<br>");
}
$payObj["appId"] = $this->_appid;
$payObj["package"] = $this->getPackageSign();
$payObj["timeStamp"] = $this->getParams(self::TIMESTAMP);
$payObj["nonceStr"] = $this->getParams(self::OUT_TRADE_NO);
$payObj["paySign"] = $this->getPaySign($payObj);
$payObj["signType"] = $this->_signtype;
return json_encode($payObj);
} catch (Exception $e) {
die($e->getMessage());
}
}
/*
* 构建发货状态数组 主要三个参数openid transid orderid
*/
public function createDeliverPost(Array $params) {
$deliver = array();
$deliver['appid'] = $this->_appid;
$deliver['openid'] = $params['openid'];
$deliver['transid'] = $params['transid'];
$deliver['out_trade_no'] = $params['out_trade_no'];
$deliver['deliver_timestamp'] = current_time('timestamp');
$deliver['deliver_status'] = 1;
$deliver['deliver_msg'] = 'OK';
$deliver['app_signature'] = $this->getPaySign($deliver);
$deliver['sign_method'] = 'sha1';
return $deliver;
}
/*
* 生成扫描或者点击原生URL后,响应的XML格式
* @params $retcode $reterrmsg 定义该商品的状态
*/
public function createNativePackage($retcode = 0, $reterrmsg = "ok") {
try {
if ($this->checkParams() == false && $retcode == 0) { //如果是正常的返回, 检查财付通的参数
throw new Exception("生成package参数缺失!" . "<br>");
}
$nativeObj["AppId"] = $this->_appid;
$nativeObj["Package"] = $this->getPackageSign();
$nativeObj["TimeStamp"] = $this->getParams(self::TIMESTAMP);
$nativeObj["NonceStr"] = $this->getParams(self::OUT_TRADE_NO);
$nativeObj["RetCode"] = $retcode;
$nativeObj["RetErrMsg"] = $reterrmsg;
$nativeObj["AppSignature"] = $this->getPaySign($nativeObj);
$nativeObj["SignMethod"] = $this->_signtype;
$commonUtil = new CommonUtil();
$xml = $commonUtil->arrayToXml($nativeObj);
exit($xml);
}catch (Exception $e) {
echo ($e->getMessage());
}
}
/*
* 生成原生URL 以订单号为参数 这是灌穿整个支付流程
*/
public function createNativeUrl($productid) {
$commonUtil = new CommonUtil();
$nativeObj["appid"] = $this->_appid;
$nativeObj["productid"] = urlencode($productid);
$nativeObj["timestamp"] = time();
$nativeObj["nonceStr"] = commonUtil::createNoncestr();
$nativeObj["sign"] = $this->getPaySign($nativeObj);
$nativeString = $commonUtil->formatPayUrlQuery($nativeObj, false);
return "weixin://wxpay/bizpayurl?".$nativeString;
}
/*
* 取IP地址
*/
public function getIp(){
switch(true) {
case !empty($_SERVER["HTTP_CLIENT_IP"]):
$ip = $_SERVER["HTTP_CLIENT_IP"];
break;
case !empty($_SERVER["HTTP_X_FORWARDED_FOR"]):
$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
break;
case !empty($_SERVER["REMOTE_ADDR"]):
$ip = $_SERVER["REMOTE_ADDR"];
break;
default:
$ip = "127.0.0.1";
}
return $ip;
}
}
class MD5SignUtil {
public function sign($content, $key) {
try {
if (null == $key) {
throw new Exception("财付通签名key不能为空!" . "<br>");
}
if (null == $content) {
throw new Exception("财付通签名内容不能为空" . "<br>");
}
$signStr = $content . "&key=" . $key;
return strtoupper(md5($signStr));
} catch (Exception $e) {
echo ($e->getMessage());
}
}
public static function verifySignature($content, $sign, $md5Key) {
$signStr = $content . "&key=" . $md5Key;
$calculateSign = strtolower(md5($signStr));
$tenpaySign = strtolower($sign);
return $calculateSign == $tenpaySign;
}
}
class CommonUtil {
public function genAllUrl($toURL, $paras) {
$allUrl = null;
if (null == $toURL) {
die("toURL is null");
}
if (strripos($toURL,"?") =="") {
$allUrl = $toURL . "?" . $paras;
} else {
$allUrl = $toURL . "&" . $paras;
}
return $allUrl;
}
//订单号,可根据实际自定义
static public function createOrderNo() {
$nonce = CommonUtil::createNoncestr(4);
return strtoupper(date('ymds').substr(microtime(),2,4).$nonce);
}
//随机字符串
static public 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;
}
public function splitParaStr($src, $token) {
$resMap = array();
$items = explode($token,$src);
foreach ($items as $item){
$paraAndValue = explode("=",$item);
if ($paraAndValue != "") {
$resMap[$paraAndValue[0]] = $paraAndValue[1];
}
}
return $resMap;
}
static function trimString($value) {
$ret = null;
if (null != $value) {
$ret = $value;
if (strlen($ret) == 0) {
$ret = null;
}
}
return $ret;
}
public function formatUrlQuery($paraMap, $urlencode) {
$buff = "";
ksort($paraMap, SORT_STRING);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v && "sign" != $k) {
if($urlencode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
public function formatPayUrlQuery($paraMap, $urlencode) {
$buff = "";
ksort($paraMap, SORT_STRING);
foreach ($paraMap as $k => $v) {
if($urlencode){
$v = urlencode($v);
}
$buff .= strtolower($k) . "=" . $v . "&";
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
/*
* 输出一级数组的xml格式
*/
public function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key=>$val) {
if ($key == 'TimeStamp' || $key == 'RetCode') {
$xml.="<".$key.">".$val."</".$key.">";
} else
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
$xml .= "</xml>";
return $xml;
}
}
根据《微信JS-SDK地理位置接口例子》中的QQMapModel和ImageCacheModel类进行扩展。看了下腾讯地图有个静态图的V2版,我顺便也加上去,继续围绕腾讯地图把取街景的接口写上。由于是Demo,像取街景有几个参数可以自行定义,我只用默认!不多说看码吧
QQMapModel.class.php: (注:API_KEY 用QQ在官方申请,目前免费)
<?php
namespace Home\Model;
class QQMapModel {
const
PANO_API = 'http://apis.map.qq.com/ws/streetview/v1/getpano',
API_KEY = 'CZQBZ-RC53V-2RQPX-UFNBE-FCH2J-DF00';
static public function call($url, array $params = null) {
$url = $url.'?'.http_build_query($params);
$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,
));
$result = curl_exec($ch);
if (curl_errno($ch)) {
return false;
}
curl_close($ch);
return $result;
}
//新静态图v2接口
static function staticMap($point, $otherParam = array()) {
$pos = explode(',', $point);
$posStr = $pos[1].','.$pos[0];
$param = array(
'size' => '620*380',
'center' => $posStr,
'zoom' => 13,
'format' => 'png',
'maptype' => 'roadmap',
'markers' => $posStr,
'key' => self::API_KEY,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
return 'http://apis.map.qq.com/ws/staticmap/v2/?' . http_build_query($param);
}
//取街景图接口
static function streetView($pano, $otherParam = array()) { //max 960x640
$param = array(
'size' => '620x380',
'pano' => $pano,
'heading' => 0,
'pitch' => 0,
'key' => self::API_KEY,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
return 'http://apis.map.qq.com/ws/streetview/v1/image?' . http_build_query($param);
}
//街景图的ID接口
static function getPano($location, $otherParam = array()) {
$param = array(
'location' => $location,
'radius' => 200,
'output' => 'json',
'key' => self::API_KEY,
);
if(count($otherParam))
$param = array_merge($param, $otherParam);
$result = self::call(self::PANO_API, $param);
if ($result) {
return json_decode($result, 1);
}
return false;
}
//静态图v1版接口
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 getStreetCacheImg($points) {
$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 {
$res = QQMapModel::getPano($points);
if($res['status'] === 0) {
$pano = $res['detail']['id'];
$imageUrl = QQMapModel::streetView($pano);
self::saveCacheImg($imageUrl, $fileName);
return self::CACHE_DIR.$fileName.self::$TYPE;
}
return self::CACHE_DIR.'default'.self::$TYPE;
}
}
然后Controller里的处理:(至于Layout模版里的AJAX调用与上篇地理接口差不多,这里就不写了)
public function streetpicAction() {
layout(false);
if(I('pos','')) {
$target = ImageCacheModel::getStreetCacheImg(I('pos'));
$url = __ROOT__.$target;
redirect($url);
}
}
根据前面几个地理位置教程,最终把腾讯地图的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间的经纬度转没什么意义。这些接口就由你们来试了,一通百通~
以酒店查询标签文本的效果图:
本想这个接口很简单不想写写的,因为看文档它跟图像接口差不多。不过给新用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″,但不影响你录制时的长度播放)
相关文章
使用PHP+JavaScript将HTML页面转换为图片的实例分享
这篇文章主要介绍了使用PHP+JavaScript将HTML元素转换为图片的实例分享,文后结果的截图只能体现出替换的字体,也不能说将静态页面转为图片可以加快加载,只是这种做法比较interesting XD需要的朋友可以参考下...2016-04-19- php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 今天小编在这里就来给各位Photoshop的这一款软件的使用者们来说说把古装美女图片转为细腻的工笔画效果的制作教程,各位想知道方法的使用者们,那么下面就快来跟着小编一...2016-09-14
- 这篇文章主要介绍了Python 图片转数组,二进制互转操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-09
- 下面小编就为大家带来一篇利用JS实现点击按钮后图片自动切换的简单方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2016-10-25
- 这篇文章主要介绍了c# 三种方法调用WebService接口的相关资料,文中示例代码非常详细,帮助大家更好的理解和学习,感兴趣的朋友可以了解下...2020-07-07
jquery左右滚动焦点图banner图片鼠标经过显示上下页按钮
jquery左右滚动焦点图banner图片鼠标经过显示上下页按钮...2013-10-13- 这篇文章主要为大家详细介绍了js实现上传图片及时预览的相关资料,具有一定的参考价值,感兴趣的朋友可以参考一下...2016-05-09
- Photoshop的这一款软件小编相信很多的人都已经是使用过了吧,那么今天小编在这里就给大家带来了用Photoshop软件制作枪战电影海报的教程,想知道制作步骤的玩家们,那么下面...2016-09-14
- 这篇文章主要介绍了vue接口请求加密实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-12
- 这篇文章主要为大家详细介绍了C#微信开发之发送模板消息的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-06-25
- 图片剪裁是常用的方法,那么如何通过4坐标剪裁图片,本文就详细的来介绍一下,感兴趣的小伙伴们可以参考一下...2021-06-04
- 这篇文章主要介绍了iOS新版微信底部返回横条问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-30
- 共享一段使用PHP下载CSS文件中的图片的代码 复制代码 代码如下: <?php //note 设置PHP超时时间 set_time_limit(0); //note 取得样式文件内容 $styleFileContent = file_get_contents('images/style.css'); //not...2013-10-04
- 这篇文章主要给大家介绍了关于微信小程序如何获取图片宽度与高度的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-10
- PHP代码如下:复制代码 代码如下:if (isset($_FILES["Filedata"]) || !is_uploaded_file($_FILES["Filedata"]["tmp_name"]) || $_FILES["Filedata"]["error"] != 0) { $upload_file = $_FILES['Filedata']; $fil...2013-10-04
- ps软件是现在很多人比较喜欢的,有着非常不错的使用效果,这次文章就给大家介绍下ps怎么制作图片阴影效果,还不知道制作方法的赶紧来看看。 ps图片阴影效果怎么做方法/...2017-07-06
- 为公司系统业务需要,这几天了解了一下微信和支付宝扫码支付的接口,并用c#实现了微信和支付宝扫码支付的功能。需要的朋友跟随小编一起看看吧...2020-06-25
C#中图片旋转和翻转(RotateFlipType)用法分析
这篇文章主要介绍了C#中图片旋转和翻转(RotateFlipType)用法,实例分析了C#图片旋转及翻转Image.RotateFlip方法属性的常用设置技巧,需要的朋友可以参考下...2020-06-25