示例效果图:

unity 微信云储存_json

1、开通COS对象存储服务

PS:建议选择使用七牛云,七牛云是完全免费的;腾讯云COS有半年免费50G的存储服务,但腾讯云的流量是收费的,每天都给你发扣费短信,真是烦死人 ;  阿里云的OSS或者华为云的OBS都要收费(不推荐)。

unity 微信云储存_腾讯云_02

2、创建密钥

unity 微信云储存_json_03

 

unity 微信云储存_json_04

3、thinkphp后端:计算腾讯云COS对象存储签名  

特别说明:由于签名计算放在前端会暴露 SecretId 和 SecretKey, 所以我们把签名计算过程放在后端实现,前端通过 ajax 向后端获取签名结果,正式部署时请在后端加一层自己网站本身的权限检验。

thinkphp控制器controller中的代码:

<?php
namespace app\index\controller;
use think\Controller;
use think\Request;
/*引入腾讯云cos类库(extend/sts.php)*/
import('sts', EXTEND_PATH);

class Car extends Controller{
        /*
	 * 获取腾讯云COS对象存储签名
	 * 官网:https://cloud.tencent.com/product/cos
	*/
	public function getSts(){
		$sts = new \STS();
		// 配置参数
		$config = array(
			'url' => 'https://sts.tencentcloudapi.com/',
			'domain' => 'sts.tencentcloudapi.com',
			'proxy' => '',
			'secretId' => '你cos密钥中的secretId', // 固定密钥
			'secretKey' => '你cos密钥中的secretKey',    // 固定密钥
			'bucket' => 'myfaka-1256433534',//你的存储桶名称bucket
			'region' => 'ap-guangzhou', // bucket所在地域
			'durationSeconds' => 1800, // 密钥有效期
			'allowPrefix' => '*', // 这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的目录,例子:* 或者 a/* 或者 a.jpg
			// 密钥的权限列表。简单上传需要以下的权限,其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
			'allowActions' => array (
				//上传权限
				'name/cos:PutObject',
				'name/cos:PostObject',
				//下载权限 
                                "name/cos:GetObject",
				//查询权限
				"name/cos:GetBucket",
				"name/cos:HeadObject",
				//删除权限				
				"name/cos:DeleteObject",
			)
		);
		// 获取临时密钥,计算签名
		$tempKeys = $sts->getTempKeys($config);

		// 返回数据给前端
		header('Content-Type: application/json');
		header('Access-Control-Allow-Origin: http://127.0.0.1'); // 这里修改允许跨域访问的网站
		header('Access-Control-Allow-Headers: origin,accept,content-type');
		echo json_encode($tempKeys);
	}
}

?>

extend/sts.php的代码:(这是官方提供的类)

<?php

/**
 * 代码出处:
 * https://github.com/tencentyun/qcloud-cos-sts-sdk
 */

class STS{
    // 临时密钥计算样例

    function _hex2bin($data) {
        $len = strlen($data);
        return pack("H" . $len, $data);
    }
    // obj 转 query string
    function json2str($obj, $notEncode = false) {
        ksort($obj);
        $arr = array();
        if(!is_array($obj)){
            throw new Exception($obj + " must be a array");
        }
        foreach ($obj as $key => $val) {
            array_push($arr, $key . '=' . ($notEncode ? $val : rawurlencode($val)));
        }
        return join('&', $arr);
    }
    // 计算临时密钥用的签名
    function getSignature($opt, $key, $method, $config) {
        $formatString = $method . $config['domain'] . '/?' . $this->json2str($opt, 1);
        $sign = hash_hmac('sha1', $formatString, $key);
        $sign = base64_encode($this->_hex2bin($sign));
        return $sign;
    }
    // v2接口的key首字母小写,v3改成大写,此处做了向下兼容
    function backwardCompat($result) {
        if(!is_array($result)){
            throw new Exception($result + " must be a array");
        }
        $compat = array();
        foreach ($result as $key => $value) {
            if(is_array($value)) {
                $compat[lcfirst($key)] = $this->backwardCompat($value);
            } elseif ($key == 'Token') {
                $compat['sessionToken'] = $value;
            } else {
                $compat[lcfirst($key)] = $value;
            }
        }
        return $compat;
    }
    // 获取临时密钥
    function getTempKeys($config) {
        if(array_key_exists('bucket', $config)){
            $ShortBucketName = substr($config['bucket'],0, strripos($config['bucket'], '-'));
            $AppId = substr($config['bucket'], 1 + strripos($config['bucket'], '-'));
        }
        if(array_key_exists('policy', $config)){
            $policy = $config['policy'];
        }else{
            $policy = array(
                'version'=> '2.0',
                'statement'=> array(
                    array(
                        'action'=> $config['allowActions'],
                        'effect'=> 'allow',
                        'principal'=> array('qcs'=> array('*')),
                        'resource'=> array(
                            'qcs::cos:' . $config['region'] . ':uid/' . $AppId . ':prefix//' . $AppId . '/' . $ShortBucketName . '/' . $config['allowPrefix']
                        )
                    )
                )
            );
        }
        $policyStr = str_replace('\\/', '/', json_encode($policy));
        $Action = 'GetFederationToken';
        $Nonce = rand(10000, 20000);
        $Timestamp = time();
        $Method = 'POST';
        $params = array(
            'SecretId'=> $config['secretId'],
            'Timestamp'=> $Timestamp,
            'Nonce'=> $Nonce,
            'Action'=> $Action,
            'DurationSeconds'=> $config['durationSeconds'],
            'Version'=>'2018-08-13',
            'Name'=> 'cos',
            'Region'=> 'ap-guangzhou',
            'Policy'=> urlencode($policyStr)
        );
        $params['Signature'] = $this->getSignature($params, $config['secretKey'], $Method, $config);
        $url = $config['url'];
        $ch = curl_init($url);
        if(array_key_exists('proxy', $config)){
            $config['proxy'] && curl_setopt($ch, CURLOPT_PROXY, $config['proxy']);
        }
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this->json2str($params));
        $result = curl_exec($ch);
        if(curl_errno($ch)) $result = curl_error($ch);
        curl_close($ch);
        $result = json_decode($result, 1);
        if (isset($result['Response'])) {
            $result = $result['Response'];
            $result['startTime'] = $result['ExpiredTime'] - $config['durationSeconds'];
        }
        $result = $this->backwardCompat($result);
        return $result;
    }

    // get policy
    function getPolicy($scopes){
        if (!is_array($scopes)){
            return null;
        }
        $statements = array();

        for($i=0, $counts=count($scopes); $i < $counts; $i++){
            $actions=array();
            $resources = array();
            array_push($actions, $scopes[$i]->get_action());
            array_push($resources, $scopes[$i]->get_resource());
            $principal = array(
                'qcs' => array('*')
            );
            $statement = array(
                'actions' => $actions,
                'effect' => 'allow',
                'principal' => $principal,
                'resource' => $resources
            );
            array_push($statements, $statement);
        }

        $policy = array(
            'version' => '2.0',
            'statement' => $statements
        );
        return $policy;
    }
}

class Scope{
    var $action;
    var $bucket;
    var $region;
    var $resourcePrefix;
    function __construct($action, $bucket, $region, $resourcePrefix){
        $this->action = $action;
        $this->bucket = $bucket;
        $this->region = $region;
        $this->resourcePrefix = $resourcePrefix;
    }
    function get_action(){
        return $this->action;
    }

    function get_resource(){
        $index = strripos($this->bucket, '-');
        $bucketName = substr($this->bucket, 0, $index);
        $appid = substr($this->bucket, $index + 1);
        if(!(strpos($this->resourcePrefix, '/') === 0)){
            $this->resourcePrefix = '/' . $this->resourcePrefix;
        }
        return 'qcs::cos:' . $this->region . ':uid/' . $appid . ':prefix//' . $appid . '/' . $bucketName . $this->resourcePrefix;
    }
}
?>

4、下载cos-wx-sdk-v5.js

下载地址:https://github.com/tencentyun/cos-wx-sdk-v5/blob/master/demo/lib/cos-wx-sdk-v5.js

5、微信小程序创建config.js,用于保存对象存储参数,代码如下:

module.exports = {
    stsUrl: 'https://后端网址/car/getSts.html',//后端获取签名
    Bucket: 'myfaka-1256433534',//存储桶名称
    Region: 'ap-guangzhou',//所属地域
};

把cos-wx-sdk-v5.js和config.js都放到项目里:

unity 微信云储存_php_05

6、index.wxml代码:

<!--图片上传-->
  <view class="container">
      <van-uploader file-list="{{ fileList }}" upload-text="添加图片" bind:after-read="afterRead" bind:delete="delFile"multiple="{{true}}" />
  </view>

特别说明:这里使用的是vant-weapp的文件上传组件,vant框架地址:https://github.com/youzan/vant-weapp

7、index.js代码:

//获取应用实例
const app = getApp()

var COS = require('./cos-wx-sdk-v5')
var config = require('./config');
var toastMsg = '';
//初始化COS对象
var cos = new COS({
  // 获取签名
  getAuthorization: function(options, callback) {
    wx.request({
      url: config.stsUrl, // 服务端获取签名
      dataType: 'json',
      success: function(result) {
        var data = result.data;
        var credentials = data.credentials;
        callback({
          TmpSecretId: credentials.tmpSecretId,
          TmpSecretKey: credentials.tmpSecretKey,
          XCosSecurityToken: credentials.sessionToken,
          ExpiredTime: data.expiredTime,
        });
      }
    });
  }
});
Page({
  /**
   * 页面的初始数据
   */
  data: {
    fileList: [],
    date: ''
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {
    //获取时间,作为图片文件夹名,如20191207
    this.setData({
      date: app.globalData.util.dateFormat(new Date(), "YMD")
    });
    //清除缓存
    //wx.removeStorageSync('fileList');
    //获取缓存中的地址
    this.updateData();
  },
  afterRead(event) {
    toastMsg = "上传";
    var that = this;
    // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
    /* 单个上传 */
    /*
    const { file } = event.detail;
    var filePath = file.path;
    var filename = new Date().getTime() + '.'+ filePath.substr(filePath.lastIndexOf('.') + 1);
    //文件相对路径名
    var relativePath = 'upload/' + that.data.date + '/' + filename;
    cos.postObject({
      Bucket: config.Bucket,
      Region: config.Region,
      Key: relativePath,
      FilePath: filePath,
      onProgress: function (info) {
      }
    }, requestCallback);
     //添加到预览中
      var img = {
        id: i,
        url: app.globalData.cosUrl + relativePath,
        name: filename
      }
      //读取缓存
      let list = wx.getStorageSync('fileList');
      if (list) {
        list.push(img);
      } else {
        list = [img];
      }
      //存入缓存
      wx.setStorageSync('fileList', list);
      //延迟更新数据
      setTimeout(function () {
        that.updateData();
      }, 5000);
    */
    /* 批量上传 */
    var files = event.detail.file; //数组
    for (var i = 0; i < files.length; i++) {
      var filePath = files[i].path;
      var filename = new Date().getTime() + '.' + filePath.substr(filePath.lastIndexOf('.') + 1);
      //文件相对路径名
      var relativePath = 'upload/' + that.data.date + '/' + filename;
      cos.postObject({
        Bucket: config.Bucket,
        Region: config.Region,
        Key: relativePath,
        FilePath: filePath,
        onProgress: function(info) {
        }
      }, requestCallback);
      //添加到预览中
      var img = {
        id: i,
        url: app.globalData.cosUrl + relativePath,
        name: filename
      }
      //读取缓存
      let list = wx.getStorageSync('fileList');
      if (list) {
        list.push(img);
      } else {
        list = [img];
      }
      //存入缓存
      wx.setStorageSync('fileList', list);
    }
    //延迟更新数据
    setTimeout(function () {     
      that.updateData();
    }, 5000);
  },
  delFile(event) {
    toastMsg = "删除";
    var that = this;
    wx.showModal({
      title: '提示',
      content: '确定要删除这张图片吗?',
      success(res) {
        if (res.confirm) {
          var index = event.detail.index;
          //读取缓存
          let list = wx.getStorageSync('fileList');
          var filename = list[index].name;
          //更新fileList中的数据
          for (let i = 0; i < list.length; i++) {
            //如果item是选中的话,就删除它。
            if (filename == list[i].name) {
              // 删除对应的索引
              list.splice(i, 1);
              break;
            }
          }
          //更新缓存
          wx.setStorageSync('fileList', list);
          //更新数据
          that.updateData();
          //删除cos对象存储中的图片
          cos.deleteObject({
            Bucket: config.Bucket,
            Region: config.Region,
            Key: 'upload/' + that.data.date + '/' + filename,
          }, requestCallback);
        } else if (res.cancel) {
          //console.log('用户点击取消')
        }
      }
    })
  },
  //更新数据
  updateData() {
    this.setData({
      fileList: wx.getStorageSync('fileList')
    });
  },
})

// 回调函数
var requestCallback = function(err, data) {
  //console.log(err || data);
  if (err && err.error) {
    wx.showModal({
      title: '返回错误',
      content: '请求失败:' + (err.error.Message || err.error) + ';状态码:' + err.statusCode,
      showCancel: false
    });
  } else if (err) {
    wx.showModal({
      title: '返回错误',
      content: '请求出错:' + err + ';状态码:' + err.statusCode,
      showCancel: false
    });
  } else {
    wx.showToast({
      title: toastMsg + '成功',
      icon: 'success',
      duration: 3000
    });
  }
};

util.js时间格式化的代码:

const formatTime = date => {
  const year = date.getFullYear()
  const month = date.getMonth() + 1
  const day = date.getDate()
  const hour = date.getHours()
  const minute = date.getMinutes()
  const second = date.getSeconds()

  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}

const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : '0' + n
}

/**
 * 时间戳转化为年 月 日 时 分 秒
 * number: 传入时间戳
 * format:返回格式,支持自定义,但参数必须与formateArr里保持一致
*/
function dateFormat(number, format) {
  var formateArr = ['Y', 'M', 'D', 'h', 'm', 's'];
  var returnArr = [];
  var date = new Date(number);
  returnArr.push(date.getFullYear());
  returnArr.push(formatNumber(date.getMonth() + 1));
  returnArr.push(formatNumber(date.getDate()));

  returnArr.push(formatNumber(date.getHours()));
  returnArr.push(formatNumber(date.getMinutes()));
  returnArr.push(formatNumber(date.getSeconds()));

  for (var i in returnArr) {
    format = format.replace(formateArr[i], returnArr[i]);
  }
  return format;
}


module.exports = {
  formatTime: formatTime,
  dateFormat: dateFormat
}