整体流程简介:
1. 前端引入cos的SDK文件
2. 监听上传控件,并在图片加载至网页临时流中发起署名要求
3.后端根据上传的要求办法和路径参数返回署名Authorization和token XCosSecurityToken
4.前端再根据返回的参数和SDK以3的要求办法上传图片。
PHP署名返回流程:
1.在腾讯云的建好存储桶并配置CORS规则https://cloud.tencent.com/document/product/436/11459
2.在平台上拿到Bucket(存储桶),Region(地域),SecretId,SecretKey等参数。
3.利用(SecretId,Timestamp…)参数进行署名通过腾讯云的接口获取临时密钥,返回给前真个token也在临时密钥中
4.根据前端传的(上传要求办法,路径)和临时密钥进行署名(前端上传所利用的)并返回。
一、PHP获取署名部分(tp5)
<?php// +----------------------------------------------------------------------// | When work is a pleasure, life is a joy!// +----------------------------------------------------------------------// | User: 傅超| Email:1741108471@qq.com | Time:2018/04/21 17:55// +----------------------------------------------------------------------// | TITLE: 小程序接口// +----------------------------------------------------------------------namespace app\v1\controller;use think\Request;use think\Db;use app\v1\location\Location;use think\Cache;use \app\v1\auth\AccessToken;use \app\v1\extend\Loginlog;// 返回数据给前端header('Content-Type: application/json');header('Allow-Control-Allow-Origin: '); // 这里修正许可跨域访问的网站// header('Allow-Control-Allow-Origin: http://127.0.0.1'); // 这里修正许可跨域访问的网站//header('Allow-Control-Allow-Origin: http://mer.runmoneyin.com'); // 这里修正许可跨域访问的网站header('Allow-Control-Allow-Headers: origin,accept,content-type');/ Class Cosauth @title 获取腾讯云cos署名接口 @url http://119.29.10.64/v1/Cosauth @desc 小程序接口包含:获取上传图片署名 @version 1.0 /class Cosauth extends Base{ // 附加方法 protected $extraActionList = ['getCosAuth', 'getCosAuth']; // 跳过验证方法 protected $skipAuthActionList = ['getCosAuth', 'getCosAuthEsay']; // appid //protected $appid = 'wx4c0e1852239664b4'; // cos配置参数 protected $config = array( 'Url' => 'https://sts.api.qcloud.com/v2/index.php', 'Domain' => 'sts.api.qcloud.com', 'Proxy' => '', 'SecretId' => 'AKBLK9nF5dZL', // 固定密钥 'SecretKey' => 'jHj5GIUcqJu', // 固定密钥 'Bucket' => 'activity-1255484416', // 存储桶 'Region' => 'ap-guangzhou', 'AllowPrefix' => '', // 这里改成许可的路径前缀,这里可以根据自己网站的用户登录态判断许可上传的目录,例子: 或者 a/ 或者 a.jpg ); / @title 获取署名入口 http://119.29.10.64/v1/Cosauth/getCosAuth / public function getCosAuth() { // $data['say'] = 'hello'; // echo json_encode($data); // die; // 缓存临时密钥 if (!isset($_SESSION['tempKeysCache'])) { $_SESSION['tempKeysCache'] = array( 'policyStr' => '', 'expiredTime' => 0 ); } // 获取前端过来的参数 // $method = isset($_GET['method']) ? $_GET['method'] : 'get'; // $pathname = isset($_GET['pathname']) ? $_GET['pathname'] : '/'; $method = input('method') ? input('method') : 'post'; $pathname = input('pathname') ? input('pathname') : '/'; $callback = input('callback') ? input('callback') : ''; // 前端跨域的jsonp参数(可忽略) // 获取临时密钥,打算署名 $tempKeys = $this->getTempKeys(); if ($tempKeys && $tempKeys['credentials']) { // $datas = $this->getAuthorization($tempKeys, $method, $pathname); // echo json_encode($datas); // die; $data = array( 'Authorization' => $this->getAuthorization($tempKeys, $method, $pathname), 'XCosSecurityToken' => $tempKeys['credentials']['sessionToken'], ); } else { $data = array('error'=> $tempKeys); } //echo $callback . '(' . json_encode($data) . ')'; // 通过回调返回给其他域(可忽略) echo json_encode($data); // 正常写法的返回 die; } // json 转 query string public function json2str($obj, $notEncode = false) { ksort($obj); $arr = array(); foreach ($obj as $key => $val) { !$notEncode && ($val = urlencode($val)); array_push($arr, $key . '=' . $val); } return join('&', $arr); } // 打算临时密钥用的署名 public function getSignature($opt, $key, $method) { //global $config; $formatString = $method . $this->config['Domain'] . '/v2/index.php?' . $this->json2str($opt, 1); $sign = hash_hmac('sha1', $formatString, $key); $sign = base64_encode(hex2bin($sign)); return $sign; } // 获取临时密钥 public function getTempKeys() { //global $config; // 判断是否修正了 AllowPrefix if ($this->config['AllowPrefix'] === '_ALLOW_DIR_/') { return array('error'=> '请修正 AllowPrefix 配置项,指定许可上传的路径前缀'); } $ShortBucketName = substr($this->config['Bucket'],0, strripos($this->config['Bucket'], '-')); $AppId = substr($this->config['Bucket'], 1 + strripos($this->config['Bucket'], '-')); $policy = array( 'version'=> '2.0', 'statement'=> array( array( 'action'=> array( // 大略文件操作 'name/cos:PutObject', 'name/cos:PostObject', 'name/cos:AppendObject', 'name/cos:GetObject', 'name/cos:HeadObject', 'name/cos:OptionsObject', 'name/cos:PutObjectCopy', 'name/cos:PostObjectRestore', // 分片上传操作 'name/cos:InitiateMultipartUpload', 'name/cos:ListMultipartUploads', 'name/cos:ListParts', 'name/cos:UploadPart', 'name/cos:CompleteMultipartUpload', 'name/cos:AbortMultipartUpload', ), 'effect'=> 'allow', 'principal'=> array('qcs'=> array('')), 'resource'=> array( 'qcs::cos:' . $this->config['Region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/', 'qcs::cos:' . $this->config['Region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/' . $this->config['AllowPrefix'] ) ) ) ); $policyStr = str_replace('\\/', '/', json_encode($policy)); $Action = 'GetFederationToken'; $Nonce = rand(10000, 20000); $Timestamp = time() - 1; $Method = 'GET'; $params = array( 'Action'=> $Action, 'Nonce'=> $Nonce, 'Region'=> '', 'SecretId'=> $this->config['SecretId'], 'Timestamp'=> $Timestamp, 'durationSeconds'=> 7200, 'name'=> '', 'policy'=> $policyStr ); $params['Signature'] = urlencode($this->getSignature($params, $this->config['SecretKey'], $Method)); $url = $this->config['Url'] . '?' . $this->json2str($params, 1); $ch = curl_init($url); $this->config['Proxy'] && curl_setopt($ch, CURLOPT_PROXY, $this->config['Proxy']); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $result = curl_exec($ch); if(curl_errno($ch)) $result = curl_error($ch); curl_close($ch); $result = json_decode($result, 1); $_SESSION['tempKeysCache'] = $result['data']; $_SESSION['tempKeysCache']['policyStr'] = $policyStr; return $result['data']; } // 打算 COS API 要求用的署名 public function getAuthorization($keys, $method, $pathname) { // 获取个人 API 密钥 https://console.qcloud.com/capi $SecretId = $keys['credentials']['tmpSecretId']; $SecretKey = $keys['credentials']['tmpSecretKey']; // 整理参数 $query = array(); $headers = array(); $method = strtolower($method ? $method : 'get'); $pathname = $pathname ? $pathname : '/'; substr($pathname, 0, 1) != '/' && ($pathname = '/' . $pathname); // 工具方法 function getObjectKeys($obj) { $list = array_keys($obj); sort($list); return $list; } function obj2str($obj) { $list = array(); $keyList = getObjectKeys($obj); $len = count($keyList); for ($i = 0; $i < $len; $i++) { $key = $keyList[$i]; $val = $obj[$key] ? $obj[$key] : ''; $key = strtolower($key); $list[] = rawurlencode($key) . '=' . rawurlencode($val); } return implode('&', $list); } // 署名有效起止韶光 $now = time() - 1; $expired = $now + 3600; // 署名过期时候,600 秒后 // 要用到的 Authorization 参数列表 $qSignAlgorithm = 'sha1'; $qAk = $SecretId; $qSignTime = $now . ';' . $expired; $qKeyTime = $now . ';' . $expired; $qHeaderList = strtolower(implode(';', getObjectKeys($headers))); $qUrlParamList = strtolower(implode(';', getObjectKeys($query))); // 署名算法解释文档:https://www.qcloud.com/document/product/436/7778 // 步骤一:打算 SignKey $signKey = hash_hmac(\公众sha1\"大众, $qKeyTime, $SecretKey); // 步骤二:构成 FormatString $formatString = implode(\公众\n\"大众, array(strtolower($method), $pathname, obj2str($query), obj2str($headers), '')); // 自定义头部(暂时关闭) // header('x-test-method', $method); // header('x-test-pathname', $pathname); // 步骤三:打算 StringToSign $stringToSign = implode(\"大众\n\"大众, array('sha1', $qSignTime, sha1($formatString), '')); // 步骤四:打算 Signature $qSignature = hash_hmac('sha1', $stringToSign, $signKey); // 步骤五:布局 Authorization $authorization = implode('&', array( 'q-sign-algorithm=' . $qSignAlgorithm, 'q-ak=' . $qAk, 'q-sign-time=' . $qSignTime, 'q-key-time=' . $qKeyTime, 'q-header-list=' . $qHeaderList, 'q-url-param-list=' . $qUrlParamList, 'q-signature=' . $qSignature )); return $authorization; } //// // cos配置参数// protected $config = array(// 'Url' => 'https://sts.api.qcloud.com/v2/index.php',// 'Domain' => 'sts.api.qcloud.com', // 'Proxy' => '',// 'SecretId' => 'AKIDwqbCewU2ZxABC3QDWp1EWrBLK9nF5dZL', // 固定密钥// 'SecretKey' => 'jHj5GIAvV8eFk3B8tSwKXYO4f0IUcqJu', // 固定密钥// 'Bucket' => 'xcx-1255484416',// 'Region' => 'ap-guangzhou',// 'AllowPrefix' => '', // 这里改成许可的路径前缀,这里可以根据自己网站的用户登录态判断许可上传的目录,例子: 或者 a/ 或者 a.jpg// );// // $config = array(// // 'Url' => 'https://sts.api.qcloud.com/v2/index.php',// // 'Domain' => 'sts.api.qcloud.com',// // 'Proxy' => '',// // 'SecretId' => 'AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 固定密钥// // 'SecretKey' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', // 固定密钥// // 'Bucket' => 'test-1250000000',// // 'Region' => 'ap-guangzhou',// // 'AllowPrefix' => '_ALLOW_DIR_/', // 必填,这里改成许可的路径前缀,这里可以根据自己网站的用户登录态判断许可上传的目录,例子: 或者 a/ 或者 a.jpg// // );// // json 转 query string// function json2str($obj, $notEncode = false) // {// ksort($obj);// $arr = array();// foreach ($obj as $key => $val) {// !$notEncode && ($val = urlencode($val));// array_push($arr, $key . '=' . $val);// }// return join('&', $arr);// }// // 打算临时密钥用的署名// function getSignature($opt, $key, $method) // {// $formatString = $method . $this->config['Domain'] . '/v2/index.php?' . self::json2str($opt, 1);// $sign = hash_hmac('sha1', $formatString, $key);// $sign = base64_encode(hex2bin($sign));// return $sign;// }// // 获取临时密钥// function getTempKeys() // {// // 判断是否修正了 AllowPrefix// if ($this->config['AllowPrefix'] === '_ALLOW_DIR_/') {// return array('error'=> '请修正 AllowPrefix 配置项,指定许可上传的路径前缀');// }// // dump($this->config);// $ShortBucketName = substr($this->config['Bucket'],0, strripos($this->config['Bucket'], '-'));// $AppId = substr($this->config['Bucket'], 1 + strripos($this->config['Bucket'], '-'));// $policy = array(// 'version'=> '2.0',// 'statement'=> array(// array(// 'action'=> array(// // // 这里可以从临时密钥的权限上掌握前端许可的操作// 'name/cos:', // 这样写可以包含下面所有权限// // // 列出所有许可的操作// // // ACL 读写// // 'name/cos:GetBucketACL',// // 'name/cos:PutBucketACL',// // 'name/cos:GetObjectACL',// // 'name/cos:PutObjectACL',// // // 大略 Bucket 操作// // 'name/cos:PutBucket',// // 'name/cos:HeadBucket',// // 'name/cos:GetBucket',// // 'name/cos:DeleteBucket',// // 'name/cos:GetBucketLocation',// // // Versioning// // 'name/cos:PutBucketVersioning',// // 'name/cos:GetBucketVersioning',// // // CORS// // 'name/cos:PutBucketCORS',// // 'name/cos:GetBucketCORS',// // 'name/cos:DeleteBucketCORS',// // // Lifecycle// // 'name/cos:PutBucketLifecycle',// // 'name/cos:GetBucketLifecycle',// // 'name/cos:DeleteBucketLifecycle',// // // Replication// // 'name/cos:PutBucketReplication',// // 'name/cos:GetBucketReplication',// // 'name/cos:DeleteBucketReplication',// // // 删除文件// // 'name/cos:DeleteMultipleObject',// // 'name/cos:DeleteObject',// // 大略文件操作// 'name/cos:PutObject',// 'name/cos:PostObject',// 'name/cos:AppendObject',// 'name/cos:GetObject',// 'name/cos:HeadObject',// 'name/cos:OptionsObject',// 'name/cos:PutObjectCopy',// 'name/cos:PostObjectRestore',// // 分片上传操作// 'name/cos:InitiateMultipartUpload',// 'name/cos:ListMultipartUploads',// 'name/cos:ListParts',// 'name/cos:UploadPart',// 'name/cos:CompleteMultipartUpload',// 'name/cos:AbortMultipartUpload',// ),// 'effect'=> 'allow',// 'principal'=> array('qcs'=> array('')),// 'resource'=> array(// 'qcs::cos:' . $this->config['Region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/',// 'qcs::cos:' . $this->config['Region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/' . $this->config['AllowPrefix']// )// )// )// );// $policyStr = str_replace('\\/', '/', json_encode($policy));// $Action = 'GetFederationToken';// $Nonce = rand(10000, 20000);// $Timestamp = time() - 1;// $Method = 'GET';// $params = array(// 'Action'=> $Action,// 'Nonce'=> $Nonce,// 'Region'=> '',// 'SecretId'=> $this->config['SecretId'],// 'Timestamp'=> $Timestamp,// 'durationSeconds'=> 7200,// 'name'=> '',// 'policy'=> $policyStr// );// $params['Signature'] = urlencode(self::getSignature($params, $this->config['SecretKey'], $Method));// $url = $this->config['Url'] . '?' . self::json2str($params, 1);// $ch = curl_init($url);// $this->config['Proxy'] && curl_setopt($ch, CURLOPT_PROXY, $this->config['Proxy']);// curl_setopt($ch, CURLOPT_HEADER, 0);// curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);// curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);// $result = curl_exec($ch);// if(curl_errno($ch)) $result = curl_error($ch);// curl_close($ch);// $result = json_decode($result, 1);// return $result['data'];// }// public function getCosAuth() // {// // 获取临时密钥,打算署名// $tempKeys = self::getTempKeys();// // 返回数据给前端// header('Content-Type: application/json');// header('Allow-Control-Allow-Origin: '); // 这里修正许可跨域访问的网站// header('Allow-Control-Allow-Headers: origin,accept,content-type');// echo json_encode($tempKeys);// }}
二、javascript上传图片部分
首先去腾旭云下载cos-js-sdk-v5.min.js并引入页面,这里只展示实现部分
// 图片上传监听事宜function uploadConver(_this,event) {for(var i = 0;i<event.target.files.length;i++) { var files = event.target.files[i]; if (!files) {tipPopup('未选择上传文件',1100,4);return;}files && uploadFile(files, function (err, data) {if(data) {$(\"大众#preImg\"大众).html(\"大众\"大众).append('<img src=\公众'+data.url+'\"大众 width=\"大众200px\公众/>');//$(\"大众#conver\"大众).val(data.url);console.log(data);}else {console.log(err);}//console.log(err || data);//document.getElementById('msg').innerText = err ? err : ('上传成功,ETag=' + data.ETag);}); //uploadFileToOss(file); //console.log(file); }}// 异步要求上传署名var getAuthorization = function (options, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.onload = function (e) { var AuthData; try { AuthData = JSON.parse(xhr.responseText) } catch (e) {} if (AuthData && AuthData.Authorization) { callback(null, { Authorization: AuthData.Authorization, XCosSecurityToken: AuthData.XCosSecurityToken, }); } else { // alert(123); console.error(AuthData); callback('获取署名出错'); } }; xhr.onerror = function (e) { callback('获取署名出错'); }; xhr.send();};// 上传文件(真正上传到COS)var uploadFile = function (file, callback) {if(file.size>102410242) {// 限定图片大小2Mlayer.open({content: '上传的图片不能超过 2MB',skin: 'msg',time: 3 });//tipPopup(\"大众上传的图片不能超过 2MB\公众,1100,3);return;}if(file.type != 'image/jpeg' && file.type != 'image/png' && file.type != 'image/gif') {// 限定图片格式layer.open({content: '上传图片的格式禁绝确',skin: 'msg',time: 3 });//tipPopup(\"大众上传图片的格式禁绝确\"大众,1100,3);return;}// 检测文件名不能含有中文汉字if(escape(file.name).indexOf('%u') != -1) {layer.open({content: '上传的文件名有中文,请重新上传',skin: 'msg',time: 3 });return;}var dirBase = 'temp2018/';var myDate = new Date();var reg = new RegExp( '/' , \"大众g\公众 );// var specilReg = /[@#\$\!()%-~+\^&\s]+/g;;// 过滤文件名的分外字符// var fileName = file.name.replace(specilReg,'');var files = (myDate.toLocaleDateString()).replace(reg,\公众\公众)+'/'+myDate.getTime()+'_'+parseInt(Math.random()100000)+'_'+file.name; var Key = dirBase+ files; // 这里指定上传目录和文件名 // 实行获取署名函数,拿到署名通过回调上传 getAuthorization({Method: 'POST', Key: Key}, function (err, info) { var fd = new FormData(); fd.append('key', Key); fd.append('Signature', info.Authorization); fd.append('Content-Type', ''); info.XCosSecurityToken && fd.append('x-cos-security-token', info.XCosSecurityToken); fd.append('file', file); var url = prefix; var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); xhr.onload = function () { if (Math.floor(xhr.status / 100) === 2) { var ETag = xhr.getResponseHeader('etag'); callback(null, {url: url+Key, ETag: ETag}); }else { callback('文件 ' + Key + ' 上传失落败,状态码:' + xhr.status); } }; xhr.onerror = function () { callback('文件 ' + Key + ' 上传失落败,请检讨是否没配置 CORS 跨域规则'); }; $(\"大众#preImg\"大众).html(\"大众\"大众).append('<span style=\公众color:red\"大众>上传中……</span>'); xhr.send(fd); });};
----------------------------------------------------------------------------------------------------------
三、微信小程序上传办法
这里小程序就演示直接自己天生署名,然后通过cos的SDK供应的函数上传。
1.config.js 单独配置
var config = { Bucket: 'busin-1256537792',//存储桶 Region: 'ap-guangzhou',//地域 SecretId: 'AKIDTtRXiiW8nLLlp39yl4LW', SecretKey: 'hslO1YxFNJ5lGh0yR7c4Qemi8VhPRhmf', //replace with yours}module.exports = config
2.实现上传功能
/author: fuchaodate: 2018-04-27desc: 小程序本地署名上传图片到腾讯云cos个人"大众年夜众号: ZEROFC_DEV/// 同样须要先引入COS的SDK,和配置文件var COS = require('../../lib/cos-wx-sdk-v5');var config = require('./config');// 实例COSvar cos = new COS({ getAuthorization: function (params, callback) {//获取署名 var authorization = COS.getAuthorization({ SecretId: config.SecretId, SecretKey: config.SecretKey, Method: params.Method, Key: params.Key }); callback(authorization); }});Page({ data: { list: [], }, simpleUpload: function() { // 选择文件 wx.chooseImage({ count: 4, // 默认9 sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { var filePath = res.tempFilePaths[0] var Key = filePath.substr(filePath.lastIndexOf('/') + 1); // 这里指定上传的文件名 var dateObj = new Date(); var timestamp = dateObj.getTime(); var nowDate = dateObj.toLocaleDateString(); var formatDate = nowDate.replace(/\//g,\"大众-\公众); // 格式斜杠日期 console.log(formatDate); var newKey = formatDate + '/' + timestamp+Key; // cos上定义目录 var tempObj = {}; tempObj.imgLocation = 'https://' + config.Bucket + '.cos.' + config.Region + '.myqcloud.com/' + newKey; // 返回上传的绝对URL // sdk供应的COS上传函数,如果想批量上传,在这里加FOR循环即可。 cos.postObject({ Bucket: config.Bucket, // 存储桶 Region: config.Region, // 地域 Key: newKey, FilePath: filePath, // 本地文件临时地址 // onProgress: function (info) { // 上传时基本信息 // console.log(JSON.stringify(info)); // } },requestCallback(null,tempObj)); } }) },});// 上传图片回调函数var requestCallback = function (err, data) { console.log(err || data); if (err && err.error) { wx.showModal({ title: '返回缺点', content: '要求失落败:' + err.error.Message + ';状态码:' + err.statusCode, showCancel: false }); } else if (err) { wx.showModal({ title: '要求出错', content: '要求出错:' + err + ';状态码:' + err.statusCode, showCancel: false }); } else { console.log(data); wx.showToast({ title: '要求成功', icon: 'success', duration: 3000 }); }};
个人避坑办理方案
缘故原由:开始上传图片到cos的项目我是用TP3.2做的,但是上传到linux且高版本的php做事器上,创造我那个天生署名的php竟然报500缺点,而我本地是windows且php版本低于5.5统统都很正常。至于是什么缘故原由,我也没韶光去办理,只能把天生署名的php文件改成tp5.0的放在另一个项目中,而我就以跨域的办法去要求署名。
1.php文件修正
// 增加跨域相应头header('Content-Type: application/json');header('Allow-Control-Allow-Origin: '); // 这里修正许可跨域访问的网站header('Allow-Control-Allow-Headers: origin,accept,content-type');// 增加前端jsonp参数$callback = input('callback') ? input('callback') : '';// 以回调函数返回署名到前端echo $callback . '(' . json_encode($data) . ')';
2.js部分修正(紧张改成跨域要求署名)
// 打算署名var getAuthorization = function (options, callback) { var baseUrl = $(\"大众#cosurl\"大众).attr(\公众urls\"大众); var url = baseUrl; $.ajax({ url:url, dataType: 'jsonp', //类型 jsonp: \"大众callback\"大众, // 参数 后台接管的是回掉函数名 //data:\公众\公众, type:'get', success: function(result) { callback(null, { Authorization: result.Authorization, // 跨域拿到署名 XCosSecurityToken: result.XCosSecurityToken, }); }, error:function(result) { callback('获取署名出错'); } })};
个人微信"大众年夜众号