作者 郭文星

123

  1 +{"license":"regular","licenseto":"44234","licensekey":"o5H8NqLjYQiTFa7C LedaRIu7u+RcuKDQofF9RQ=="}
  1 +<?php
  2 +
  3 +namespace addons\qiniu;
  4 +
  5 +use fast\Http;
  6 +use Qiniu\Auth;
  7 +use think\Addons;
  8 +use think\Loader;
  9 +
  10 +/**
  11 + * 七牛云储存插件
  12 + */
  13 +class Qiniu extends Addons
  14 +{
  15 +
  16 + /**
  17 + * 插件安装方法
  18 + * @return bool
  19 + */
  20 + public function install()
  21 + {
  22 + return true;
  23 + }
  24 +
  25 + /**
  26 + * 插件卸载方法
  27 + * @return bool
  28 + */
  29 + public function uninstall()
  30 + {
  31 + return true;
  32 + }
  33 +
  34 + /**
  35 + * 上传初始化时
  36 + */
  37 + public function uploadConfigInit(&$upload)
  38 + {
  39 + $config = $this->getConfig();
  40 +
  41 + $policy = array(
  42 + 'saveKey' => ltrim($config['savekey'], '/'),
  43 + );
  44 +
  45 + $config['savekey'] = str_replace(['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'], ['{year}', '{mon}', '{day}', '{filemd5}', '{.suffix}'], $config['savekey']);
  46 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  47 + $multipart['qiniutoken'] = $auth->uploadToken($config['bucket'], null, $config['expire'], $policy);
  48 + $upload = array_merge($upload, [
  49 + 'cdnurl' => $config['cdnurl'],
  50 + 'uploadurl' => $config['uploadmode'] == 'client' ? $config['uploadurl'] : addon_url('qiniu/index/upload', [], false, true),
  51 + 'uploadmode' => $config['uploadmode'],
  52 + 'bucket' => $config['bucket'],
  53 + 'maxsize' => $config['maxsize'],
  54 + 'mimetype' => $config['mimetype'],
  55 + 'savekey' => $config['savekey'],
  56 + 'chunking' => (bool)($config['chunking'] ?? $upload['chunking']),
  57 + 'chunksize' => (int)($config['chunksize'] ?? $upload['chunksize']),
  58 + 'multipart' => $multipart,
  59 + 'storage' => $this->getName(),
  60 + 'multiple' => $config['multiple'] ? true : false,
  61 + ]);
  62 + }
  63 +
  64 + /**
  65 + * 附件删除后
  66 + */
  67 + public function uploadDelete($attachment)
  68 + {
  69 + $config = $this->getConfig();
  70 + if ($attachment['storage'] == 'qiniu' && isset($config['syncdelete']) && $config['syncdelete']) {
  71 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  72 + $entry = $config['bucket'] . ':' . ltrim($attachment->url, '/');
  73 + $encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
  74 + $url = 'http://rs.qiniu.com/delete/' . $encodedEntryURI;
  75 + $headers = $auth->authorization($url);
  76 + //删除云储存文件
  77 + $ret = Http::sendRequest($url, [], 'POST', [CURLOPT_HTTPHEADER => ['Authorization: ' . $headers['Authorization']]]);
  78 + //如果是服务端中转,还需要删除本地文件
  79 + //if ($config['uploadmode'] == 'server') {
  80 + // $filePath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
  81 + // if ($filePath) {
  82 + // @unlink($filePath);
  83 + // }
  84 + //}
  85 + }
  86 + return true;
  87 + }
  88 + public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
  89 + {
  90 + $deadline = time() + $expires;
  91 + $scope = $bucket;
  92 + if ($key !== null) {
  93 + $scope .= ':' . $key;
  94 + }
  95 +
  96 + $args = self::copyPolicy($args, $policy, $strictPolicy);
  97 + $args['scope'] = $scope;
  98 + $args['deadline'] = $deadline;
  99 +
  100 + $b = json_encode($args);
  101 + return $this->signWithData($b);
  102 + }
  103 + public function appInit()
  104 + {
  105 + if (!class_exists('\Qiniu\Config')) {
  106 + Loader::addNamespace('Qiniu', ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS);
  107 + require_once ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS . 'functions.php';
  108 + }
  109 + }
  110 +
  111 +}
  1 +//修改上传的接口调用
  2 +require(['upload'], function (Upload) {
  3 +
  4 + //初始化中完成判断
  5 + Upload.events.onInit = function () {
  6 + //如果上传接口不是七牛云,则不处理
  7 + if (this.options.url !== Config.upload.uploadurl) {
  8 + return;
  9 + }
  10 + var _success = this.options.success;
  11 +
  12 + $.extend(this.options, {
  13 + chunkSuccess: function (chunk, file, response) {
  14 + this.contexts = this.contexts ? this.contexts : [];
  15 + this.contexts.push(typeof response.ctx !== 'undefined' ? response.ctx : response.data.ctx);
  16 + },
  17 + chunksUploaded: function (file, done) {
  18 + var that = this;
  19 + var params = $(that.element).data("params") || {};
  20 + var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
  21 + Fast.api.ajax({
  22 + url: "/addons/qiniu/index/upload",
  23 + data: {
  24 + action: 'merge',
  25 + filesize: file.size,
  26 + filename: file.name,
  27 + chunkid: file.upload.uuid,
  28 + chunkcount: file.upload.totalChunkCount,
  29 + width: file.width || 0,
  30 + height: file.height || 0,
  31 + type: file.type,
  32 + category: category,
  33 + qiniutoken: Config.upload.multipart.qiniutoken,
  34 + contexts: this.contexts
  35 + },
  36 + }, function (data, ret) {
  37 + done(JSON.stringify(ret));
  38 + return false;
  39 + }, function (data, ret) {
  40 + file.accepted = false;
  41 + that._errorProcessing([file], ret.msg);
  42 + return false;
  43 + });
  44 +
  45 + },
  46 + });
  47 +
  48 + //先移除已有的事件
  49 + this.off("success", _success).on("success", function (file, response) {
  50 + var that = this;
  51 + var ret = {code: 0, msg: response};
  52 + try {
  53 + ret = typeof response === 'string' ? JSON.parse(response) : response;
  54 + if (file.xhr.status === 200) {
  55 + if (typeof ret.key !== 'undefined') {
  56 + ret = {code: 1, msg: "", data: {url: '/' + ret.key, hash: ret.hash}};
  57 + }
  58 + console.log(ret);
  59 + var params = $(that.element).data("params") || {};
  60 + var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
  61 + Fast.api.ajax({
  62 + url: "/addons/qiniu/index/notify",
  63 + data: {name: file.name, url: ret.data.url, hash: ret.data.hash, size: file.size, width: file.width || 0, height: file.height || 0, type: file.type, category: category, qiniutoken: Config.upload.multipart.qiniutoken}
  64 + }, function () {
  65 + return false;
  66 + }, function () {
  67 + return false;
  68 + });
  69 + }
  70 + } catch (e) {
  71 + console.error(e);
  72 + }
  73 + _success.call(this, file, ret);
  74 + });
  75 +
  76 + //如果是直传模式
  77 + if (Config.upload.uploadmode === 'client') {
  78 + var _url = this.options.url;
  79 +
  80 + //分片上传时URL链接不同
  81 + this.options.url = function (files) {
  82 + this.options.headers = {"Authorization": "UpToken " + Config.upload.multipart.qiniutoken};
  83 + if (files[0].upload.chunked) {
  84 + var chunk = null;
  85 + files[0].upload.chunks.forEach(function (item) {
  86 + if (item.status === 'uploading') {
  87 + chunk = item;
  88 + }
  89 + });
  90 + if (!chunk) {
  91 + return Config.upload.uploadurl + '/mkfile/' + files[0].size;
  92 + } else {
  93 + return Config.upload.uploadurl + '/mkblk/' + chunk.dataBlock.data.size;
  94 + }
  95 + }
  96 + return _url;
  97 + };
  98 +
  99 + this.options.params = function (files, xhr, chunk) {
  100 + var params = Config.upload.multipart;
  101 + if (chunk) {
  102 + return $.extend({}, params, {
  103 + filesize: chunk.file.size,
  104 + filename: chunk.file.name,
  105 + chunkid: chunk.file.upload.uuid,
  106 + chunkindex: chunk.index,
  107 + chunkcount: chunk.file.upload.totalChunkCount,
  108 + chunkfilesize: chunk.dataBlock.data.size,
  109 + width: chunk.file.width || 0,
  110 + height: chunk.file.height || 0,
  111 + type: chunk.file.type,
  112 + });
  113 + } else {
  114 + var retParams = $.extend({}, params);
  115 + //七牛云直传使用的是token参数
  116 + retParams.token = retParams.qiniutoken;
  117 + delete retParams.qiniutoken;
  118 + return retParams;
  119 + }
  120 + };
  121 +
  122 + //分片上传时需要变更提交的内容
  123 + this.on("sending", function (file, xhr, formData) {
  124 + if (file.upload.chunked) {
  125 + var _send = xhr.send;
  126 + xhr.send = function () {
  127 + var chunk = null;
  128 + file.upload.chunks.forEach(function (item) {
  129 + if (item.status == 'uploading') {
  130 + chunk = item;
  131 + }
  132 + });
  133 + if (chunk) {
  134 + _send.call(xhr, chunk.dataBlock.data);
  135 + }
  136 + };
  137 + }
  138 + });
  139 + }
  140 + };
  141 +
  142 +});
  1 +<?php
  2 +
  3 +return [
  4 + [
  5 + 'name' => 'accessKey',
  6 + 'title' => 'accessKey',
  7 + 'type' => 'string',
  8 + 'content' => [],
  9 + 'value' => 'Eb_QX9JpdaILVbUx0ChfMfo0AFK-12J-eD0uVy25',
  10 + 'rule' => 'required',
  11 + 'msg' => '',
  12 + 'tip' => '请在个人中心 > 密钥管理中获取 > AK',
  13 + 'ok' => '',
  14 + 'extend' => '',
  15 + ],
  16 + [
  17 + 'name' => 'secretKey',
  18 + 'title' => 'secretKey',
  19 + 'type' => 'string',
  20 + 'content' => [],
  21 + 'value' => 'VeqduMD6UzUvxkAiXc6e2N6w51N_KwppZXT3Kaix',
  22 + 'rule' => 'required',
  23 + 'msg' => '',
  24 + 'tip' => '请在个人中心 > 密钥管理中获取 > SK',
  25 + 'ok' => '',
  26 + 'extend' => '',
  27 + ],
  28 + [
  29 + 'name' => 'bucket',
  30 + 'title' => 'bucket',
  31 + 'type' => 'string',
  32 + 'content' => [],
  33 + 'value' => 'shuiku',
  34 + 'rule' => 'required',
  35 + 'msg' => '',
  36 + 'tip' => '存储空间名称',
  37 + 'ok' => '',
  38 + 'extend' => '',
  39 + ],
  40 + [
  41 + 'name' => 'uploadurl',
  42 + 'title' => '上传接口地址',
  43 + 'type' => 'select',
  44 + 'content' => [
  45 + 'https://upload-z0.qiniup.com' => '华东 https://upload-z0.qiniup.com',
  46 + 'https://upload-z1.qiniup.com' => '华北 https://upload-z1.qiniup.com',
  47 + 'https://upload-z2.qiniup.com' => '华南 https://upload-z2.qiniup.com',
  48 + 'https://upload-na0.qiniup.com' => '北美 https://upload-na0.qiniup.com',
  49 + 'https://upload-as0.qiniup.com' => '东南亚 https://upload-as0.qiniup.com',
  50 + 'https://up-cn-east-2.qiniup.com' => 'https://up-cn-east-2.qiniup.com',
  51 + ],
  52 + 'value' => 'https://upload-z2.qiniup.com',
  53 + 'rule' => 'required',
  54 + 'msg' => '',
  55 + 'tip' => '推荐选择最近的地址',
  56 + 'ok' => '',
  57 + 'extend' => '',
  58 + ],
  59 + [
  60 + 'name' => 'cdnurl',
  61 + 'title' => 'CDN地址',
  62 + 'type' => 'string',
  63 + 'content' => [],
  64 + 'value' => 'https://qiniu.ynzhsk.cn',
  65 + 'rule' => 'required',
  66 + 'msg' => '',
  67 + 'tip' => '未绑定CDN的话可使用七牛分配的测试域名',
  68 + 'ok' => '',
  69 + 'extend' => '',
  70 + ],
  71 + [
  72 + 'name' => 'uploadmode',
  73 + 'title' => '上传模式',
  74 + 'type' => 'select',
  75 + 'content' => [
  76 + 'client' => '客户端直传(速度快,无备份)',
  77 + 'server' => '服务器中转(占用服务器带宽,可备份)',
  78 + ],
  79 + 'value' => 'client',
  80 + 'rule' => '',
  81 + 'msg' => '',
  82 + 'tip' => '启用服务器中转时务必配置操作员和密码',
  83 + 'ok' => '',
  84 + 'extend' => '',
  85 + ],
  86 + [
  87 + 'name' => 'serverbackup',
  88 + 'title' => '服务器中转模式备份',
  89 + 'type' => 'radio',
  90 + 'content' => [
  91 + 1 => '备份(附件管理将产生2条记录)',
  92 + 0 => '不备份',
  93 + ],
  94 + 'value' => '0',
  95 + 'rule' => '',
  96 + 'msg' => '',
  97 + 'tip' => '服务器中转模式下是否备份文件',
  98 + 'ok' => '',
  99 + 'extend' => '',
  100 + ],
  101 + [
  102 + 'name' => 'savekey',
  103 + 'title' => '保存文件名',
  104 + 'type' => 'string',
  105 + 'content' => [],
  106 + 'value' => '/uploads/$(year)$(mon)$(day)/$(etag)$(ext)',
  107 + 'rule' => 'required',
  108 + 'msg' => '',
  109 + 'tip' => '',
  110 + 'ok' => '',
  111 + 'extend' => '',
  112 + ],
  113 + [
  114 + 'name' => 'expire',
  115 + 'title' => '上传有效时长',
  116 + 'type' => 'string',
  117 + 'content' => [],
  118 + 'value' => '6000',
  119 + 'rule' => 'required',
  120 + 'msg' => '',
  121 + 'tip' => '',
  122 + 'ok' => '',
  123 + 'extend' => '',
  124 + ],
  125 + [
  126 + 'name' => 'maxsize',
  127 + 'title' => '最大可上传',
  128 + 'type' => 'string',
  129 + 'content' => [],
  130 + 'value' => '500M',
  131 + 'rule' => 'required',
  132 + 'msg' => '',
  133 + 'tip' => '',
  134 + 'ok' => '',
  135 + 'extend' => '',
  136 + ],
  137 + [
  138 + 'name' => 'mimetype',
  139 + 'title' => '可上传后缀格式',
  140 + 'type' => 'string',
  141 + 'content' => [],
  142 + 'value' => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx,mp4,mov,doc,docx,html,gltf,apk,wgt',
  143 + 'rule' => 'required',
  144 + 'msg' => '',
  145 + 'tip' => '',
  146 + 'ok' => '',
  147 + 'extend' => '',
  148 + ],
  149 + [
  150 + 'name' => 'multiple',
  151 + 'title' => '多文件上传',
  152 + 'type' => 'radio',
  153 + 'content' => [
  154 + 1 => '开启',
  155 + 0 => '关闭',
  156 + ],
  157 + 'value' => '0',
  158 + 'rule' => 'required',
  159 + 'msg' => '',
  160 + 'tip' => '',
  161 + 'ok' => '',
  162 + 'extend' => '',
  163 + ],
  164 + [
  165 + 'name' => 'thumbstyle',
  166 + 'title' => '缩略图样式',
  167 + 'type' => 'string',
  168 + 'content' => [],
  169 + 'value' => '',
  170 + 'rule' => '',
  171 + 'msg' => '',
  172 + 'tip' => '用于附件管理缩略图样式,可使用:?imageView2/2/w/120/h/90/q/80',
  173 + 'ok' => '',
  174 + 'extend' => '',
  175 + ],
  176 + [
  177 + 'name' => 'chunking',
  178 + 'title' => '分片上传',
  179 + 'type' => 'radio',
  180 + 'content' => [
  181 + 1 => '开启',
  182 + 0 => '关闭',
  183 + ],
  184 + 'value' => '0',
  185 + 'rule' => 'required',
  186 + 'msg' => '',
  187 + 'tip' => '',
  188 + 'ok' => '',
  189 + 'extend' => '',
  190 + ],
  191 + [
  192 + 'name' => 'chunksize',
  193 + 'title' => '分片大小',
  194 + 'type' => 'number',
  195 + 'content' => [],
  196 + 'value' => '4194304',
  197 + 'rule' => 'required',
  198 + 'msg' => '',
  199 + 'tip' => '固定大小,不能修改',
  200 + 'ok' => '',
  201 + 'extend' => 'readonly',
  202 + ],
  203 + [
  204 + 'name' => 'syncdelete',
  205 + 'title' => '附件删除时是否同步删除文件',
  206 + 'type' => 'bool',
  207 + 'content' => [],
  208 + 'value' => '1',
  209 + 'rule' => 'required',
  210 + 'msg' => '',
  211 + 'tip' => '',
  212 + 'ok' => '',
  213 + 'extend' => '',
  214 + ],
  215 +];
  1 +<?php
  2 +
  3 +namespace addons\qiniu\controller;
  4 +
  5 +use app\common\exception\UploadException;
  6 +use app\common\library\Upload;
  7 +use app\common\model\Attachment;
  8 +use Qiniu\Auth;
  9 +use Qiniu\Storage\ResumeUploader;
  10 +use Qiniu\Storage\UploadManager;
  11 +use think\addons\Controller;
  12 +use think\Config;
  13 +
  14 +/**
  15 + * 七牛管理
  16 + *
  17 + */
  18 +class Index extends Controller
  19 +{
  20 +
  21 + public function _initialize()
  22 + {
  23 + //跨域检测
  24 + check_cors_request();
  25 +
  26 + parent::_initialize();
  27 + Config::set('default_return_type', 'json');
  28 + }
  29 +
  30 + public function index()
  31 + {
  32 + Config::set('default_return_type', 'html');
  33 + $this->error("当前插件暂无前台页面");
  34 + }
  35 +
  36 + /**
  37 + * 中转上传文件
  38 + * 上传分片
  39 + * 合并分片
  40 + */
  41 + public function upload()
  42 + {
  43 + Config::set('default_return_type', 'json');
  44 +
  45 + //$this->check();
  46 + $config = get_addon_config('qiniu');
  47 + $config['savekey'] = str_replace(['{year}', '{mon}', '{day}', '{filemd5}', '{.suffix}'], ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'], $config['savekey']);
  48 +
  49 + // 构建鉴权对象
  50 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  51 +
  52 + // 生成上传 Token
  53 + $token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
  54 + // 初始化 UploadManager 对象并进行文件的上传。
  55 + $uploadMgr = new UploadManager();
  56 +
  57 + //检测删除文件或附件
  58 + $checkDeleteFile = function ($attachment, $upload, $force = false) use ($config) {
  59 + //如果设定为不备份则删除文件和记录 或 强制删除
  60 + if ((isset($config['serverbackup']) && !$config['serverbackup']) || $force) {
  61 + if ($attachment) {
  62 + $attachment->delete();
  63 + }
  64 + if ($upload) {
  65 + //文件绝对路径
  66 + $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
  67 + @unlink($filePath);
  68 + }
  69 + }
  70 + };
  71 +
  72 + $chunkid = $this->request->post("chunkid");
  73 + if ($chunkid) {
  74 + $action = $this->request->post("action");
  75 + $chunkindex = $this->request->post("chunkindex/d");
  76 + $chunkcount = $this->request->post("chunkcount/d");
  77 + $filesize = $this->request->post("filesize");
  78 + $filename = $this->request->post("filename");
  79 + if ($action == 'merge') {
  80 + $attachment = null;
  81 + $upload = null;
  82 + if ($config['uploadmode'] == 'server') {
  83 + //合并分片文件
  84 + try {
  85 + $upload = new Upload();
  86 + $attachment = $upload->merge($chunkid, $chunkcount, $filename);
  87 + } catch (UploadException $e) {
  88 + $this->error($e->getMessage());
  89 + }
  90 + }
  91 +
  92 + $contexts = $this->request->post("contexts/a", []);
  93 + $uploader = new ResumeUploader($token, null, null, $filesize);
  94 + list($ret, $err) = $uploader->setContexts($contexts)->makeFile($filename);
  95 + if ($err !== null) {
  96 + $checkDeleteFile($attachment, $upload, true);
  97 + $this->error("上传失败");
  98 + } else {
  99 + $checkDeleteFile($attachment, $upload);
  100 + $this->success("上传成功", '', ['url' => '/' . $ret['key'], 'fullurl' => cdnurl('/' . $ret['key'], true), 'hash' => $ret['hash']]);
  101 + }
  102 + } else {
  103 + //默认普通上传文件
  104 + $file = $this->request->file('file');
  105 + try {
  106 + $upload = new Upload($file);
  107 + $file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
  108 + } catch (UploadException $e) {
  109 + $this->error($e->getMessage());
  110 + }
  111 +
  112 + //上传分片文件
  113 + //$file = $this->request->file('file');
  114 + $filesize = $file->getSize();
  115 + //合并分片文件
  116 + $uploader = new ResumeUploader($token, null, fopen($file->getRealPath(), 'rb'), $filesize);
  117 + $ret = $uploader->uploadChunk($chunkindex, $file, $filesize);
  118 + $this->success("上传成功", "", $ret);
  119 + }
  120 + } else {
  121 + $attachment = null;
  122 + //默认普通上传文件
  123 + $file = $this->request->file('file');
  124 + try {
  125 + $upload = new Upload($file);
  126 +
  127 + $suffix = $upload->getSuffix();
  128 + $md5 = md5_file($file->getRealPath());
  129 +
  130 +
  131 + $search = ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'];
  132 + $replace = [date("Y"), date("m"), date("d"), $md5, '.' . $suffix];
  133 + $savekey = ltrim(str_replace($search, $replace, $config['savekey']), '/');
  134 + $size = $upload->getFile()->getSize();
  135 + $attachment = $upload->upload($savekey);
  136 + } catch (UploadException $e) {
  137 + $this->error($e->getMessage());
  138 + }
  139 +
  140 + //文件绝对路径
  141 + $filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
  142 +
  143 + //上传到七牛后保存的文件名
  144 + $saveKey = ltrim($attachment->url, '/');
  145 +
  146 + $url = $attachment->url;
  147 +
  148 + try {
  149 + // 调用 UploadManager 的 putFile 方法进行文件的上传。
  150 + list($ret, $err) = $uploadMgr->putFile($token, $saveKey, $filePath);
  151 +
  152 + if ($err !== null) {
  153 + throw new \Exception("上传失败");
  154 + }
  155 + //成功不做任何操作
  156 + } catch (\Exception $e) {
  157 + $checkDeleteFile($attachment, $upload, true);
  158 + $this->error("上传失败");
  159 + }
  160 + $hash = md5_file($filePath);
  161 + $checkDeleteFile($attachment, $upload);
  162 + $this->success("上传成功", '', ['url' => $url, 'fullurl' => cdnurl($url, true), 'hash' => $hash, 'size' => $size]);
  163 + }
  164 + }
  165 +
  166 + /**
  167 + * 通知回调
  168 + */
  169 + public function notify()
  170 + {
  171 + Config::set('default_return_type', 'json');
  172 +
  173 + $this->check();
  174 +
  175 + $size = $this->request->post('size/d');
  176 + $name = $this->request->post('name', '');
  177 + $hash = $this->request->post('hash', '');
  178 + $type = $this->request->post('type', '');
  179 + $url = $this->request->post('url', '');
  180 + $width = $this->request->post('width/d');
  181 + $height = $this->request->post('height/d');
  182 + $category = $this->request->post('category', '');
  183 + $suffix = substr($name, stripos($name, '.') + 1);
  184 + $attachment = Attachment::where('url', $url)->where('storage', 'qiniu')->find();
  185 + if (!$attachment) {
  186 + $params = array(
  187 + 'category' => $category,
  188 + 'admin_id' => (int)session('admin.id'),
  189 + 'user_id' => (int)cookie('uid'),
  190 + 'filename' => $name,
  191 + 'filesize' => $size,
  192 + 'imagewidth' => $width,
  193 + 'imageheight' => $height,
  194 + 'imagetype' => $suffix,
  195 + 'imageframes' => 0,
  196 + 'mimetype' => $type,
  197 + 'url' => $url,
  198 + 'uploadtime' => time(),
  199 + 'storage' => 'qiniu',
  200 + 'sha1' => $hash,
  201 + );
  202 + Attachment::create($params);
  203 + }
  204 + $this->success();
  205 + }
  206 +
  207 + /**
  208 + * 检查签名是否正确或过期
  209 + */
  210 + protected function check()
  211 + {
  212 + $qiniutoken = $this->request->post('qiniutoken', $this->request->server('AUTHORIZATION'), 'trim');
  213 + if (!$qiniutoken) {
  214 + $this->error("参数不正确");
  215 + }
  216 + $config = get_addon_config('qiniu');
  217 + $auth = new Auth($config['accessKey'], $config['secretKey']);
  218 + list($accessKey, $sign, $data) = explode(':', $qiniutoken);
  219 + if (!$accessKey || !$sign || !$data) {
  220 + $this->error("参数不正确");
  221 + }
  222 + if ($accessKey !== $config['accessKey']) {
  223 + $this->error("参数不正确");
  224 + }
  225 + if ($accessKey . ':' . $sign !== $auth->sign($data)) {
  226 + $this->error("签名不正确");
  227 + }
  228 + $json = json_decode(\Qiniu\base64_urlSafeDecode($data), true);
  229 + if ($json['deadline'] < time()) {
  230 + $this->error("请求已经超时");
  231 + }
  232 + }
  233 +}
  1 +name = qiniu
  2 +title = 七牛云储存
  3 +intro = 使用七牛云储存,支持直传、服务器中转、分片上传
  4 +author = FastAdmin
  5 +website = https://www.fastadmin.net
  6 +version = 1.1.4
  7 +state = 1
  8 +url = /addons/qiniu
  9 +license = regular
  10 +licenseto = 44234
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Zone;
  5 +
  6 +final class Auth
  7 +{
  8 + private $accessKey;
  9 + private $secretKey;
  10 +
  11 + public function __construct($accessKey, $secretKey)
  12 + {
  13 + $this->accessKey = $accessKey;
  14 + $this->secretKey = $secretKey;
  15 + }
  16 +
  17 + public function getAccessKey()
  18 + {
  19 + return $this->accessKey;
  20 + }
  21 +
  22 + public function sign($data)
  23 + {
  24 + $hmac = hash_hmac('sha1', $data, $this->secretKey, true);
  25 + return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
  26 + }
  27 +
  28 + public function signWithData($data)
  29 + {
  30 + $encodedData = \Qiniu\base64_urlSafeEncode($data);
  31 + return $this->sign($encodedData) . ':' . $encodedData;
  32 + }
  33 +
  34 + public function signRequest($urlString, $body, $contentType = null)
  35 + {
  36 + $url = parse_url($urlString);
  37 + $data = '';
  38 + if (array_key_exists('path', $url)) {
  39 + $data = $url['path'];
  40 + }
  41 + if (array_key_exists('query', $url)) {
  42 + $data .= '?' . $url['query'];
  43 + }
  44 + $data .= "\n";
  45 +
  46 + if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
  47 + $data .= $body;
  48 + }
  49 + return $this->sign($data);
  50 + }
  51 +
  52 + public function verifyCallback($contentType, $originAuthorization, $url, $body)
  53 + {
  54 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  55 + return $originAuthorization === $authorization;
  56 + }
  57 +
  58 + public function privateDownloadUrl($baseUrl, $expires = 3600)
  59 + {
  60 + $deadline = time() + $expires;
  61 +
  62 + $pos = strpos($baseUrl, '?');
  63 + if ($pos !== false) {
  64 + $baseUrl .= '&e=';
  65 + } else {
  66 + $baseUrl .= '?e=';
  67 + }
  68 + $baseUrl .= $deadline;
  69 +
  70 + $token = $this->sign($baseUrl);
  71 + return "$baseUrl&token=$token";
  72 + }
  73 +
  74 + public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
  75 + {
  76 + $deadline = time() + $expires;
  77 + $scope = $bucket;
  78 + if ($key !== null) {
  79 + $scope .= ':' . $key;
  80 + }
  81 +
  82 + $args = self::copyPolicy($args, $policy, $strictPolicy);
  83 + $args['scope'] = $scope;
  84 + $args['deadline'] = $deadline;
  85 +
  86 + $b = json_encode($args);
  87 + return $this->signWithData($b);
  88 + }
  89 +
  90 + /**
  91 + *上传策略,参数规格详见
  92 + *http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
  93 + */
  94 + private static $policyFields = array(
  95 + 'callbackUrl',
  96 + 'callbackBody',
  97 + 'callbackHost',
  98 + 'callbackBodyType',
  99 + 'callbackFetchKey',
  100 +
  101 + 'returnUrl',
  102 + 'returnBody',
  103 +
  104 + 'endUser',
  105 + 'saveKey',
  106 + 'insertOnly',
  107 +
  108 + 'detectMime',
  109 + 'mimeLimit',
  110 + 'fsizeMin',
  111 + 'fsizeLimit',
  112 +
  113 + 'persistentOps',
  114 + 'persistentNotifyUrl',
  115 + 'persistentPipeline',
  116 +
  117 + 'deleteAfterDays',
  118 + 'fileType',
  119 + 'isPrefixalScope',
  120 + );
  121 +
  122 + private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
  123 + {
  124 + if ($originPolicy === null) {
  125 + return array();
  126 + }
  127 + foreach ($originPolicy as $key => $value) {
  128 + if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
  129 + $policy[$key] = $value;
  130 + }
  131 + }
  132 + return $policy;
  133 + }
  134 +
  135 + public function authorization($url, $body = null, $contentType = null)
  136 + {
  137 + $authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
  138 + return array('Authorization' => $authorization);
  139 + }
  140 +
  141 + public function authorizationV2($url, $method, $body = null, $contentType = null)
  142 + {
  143 + $urlItems = parse_url($url);
  144 + $host = $urlItems['host'];
  145 +
  146 + if (isset($urlItems['port'])) {
  147 + $port = $urlItems['port'];
  148 + } else {
  149 + $port = '';
  150 + }
  151 +
  152 + $path = $urlItems['path'];
  153 + if (isset($urlItems['query'])) {
  154 + $query = $urlItems['query'];
  155 + } else {
  156 + $query = '';
  157 + }
  158 +
  159 + //write request uri
  160 + $toSignStr = $method . ' ' . $path;
  161 + if (!empty($query)) {
  162 + $toSignStr .= '?' . $query;
  163 + }
  164 +
  165 + //write host and port
  166 + $toSignStr .= "\nHost: " . $host;
  167 + if (!empty($port)) {
  168 + $toSignStr .= ":" . $port;
  169 + }
  170 +
  171 + //write content type
  172 + if (!empty($contentType)) {
  173 + $toSignStr .= "\nContent-Type: " . $contentType;
  174 + }
  175 +
  176 + $toSignStr .= "\n\n";
  177 +
  178 + //write body
  179 + if (!empty($body)) {
  180 + $toSignStr .= $body;
  181 + }
  182 +
  183 + $sign = $this->sign($toSignStr);
  184 + $auth = 'Qiniu ' . $sign;
  185 + return array('Authorization' => $auth);
  186 + }
  187 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Cdn;
  4 +
  5 +use Qiniu\Auth;
  6 +use Qiniu\Http\Error;
  7 +use Qiniu\Http\Client;
  8 +
  9 +final class CdnManager
  10 +{
  11 +
  12 + private $auth;
  13 + private $server;
  14 +
  15 + public function __construct(Auth $auth)
  16 + {
  17 + $this->auth = $auth;
  18 + $this->server = 'http://fusion.qiniuapi.com';
  19 + }
  20 +
  21 + /**
  22 + * @param array $urls 待刷新的文件链接数组
  23 + * @return array
  24 + */
  25 + public function refreshUrls(array $urls)
  26 + {
  27 + return $this->refreshUrlsAndDirs($urls, array());
  28 + }
  29 +
  30 + /**
  31 + * @param array $dirs 待刷新的文件链接数组
  32 + * @return array
  33 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  34 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  35 + */
  36 + public function refreshDirs(array $dirs)
  37 + {
  38 + return $this->refreshUrlsAndDirs(array(), $dirs);
  39 + }
  40 +
  41 + /**
  42 + * @param array $urls 待刷新的文件链接数组
  43 + * @param array $dirs 待刷新的目录链接数组
  44 + *
  45 + * @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
  46 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  47 + *
  48 + * 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
  49 + * 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
  50 + */
  51 + public function refreshUrlsAndDirs(array $urls, array $dirs)
  52 + {
  53 + $req = array();
  54 + if (!empty($urls)) {
  55 + $req['urls'] = $urls;
  56 + }
  57 + if (!empty($dirs)) {
  58 + $req['dirs'] = $dirs;
  59 + }
  60 +
  61 + $url = $this->server . '/v2/tune/refresh';
  62 + $body = json_encode($req);
  63 + return $this->post($url, $body);
  64 + }
  65 +
  66 + /**
  67 + * @param array $urls 待预取的文件链接数组
  68 + *
  69 + * @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
  70 + *
  71 + * @link http://developer.qiniu.com/article/fusion/api/refresh.html
  72 + */
  73 + public function prefetchUrls(array $urls)
  74 + {
  75 + $req = array(
  76 + 'urls' => $urls,
  77 + );
  78 +
  79 + $url = $this->server . '/v2/tune/prefetch';
  80 + $body = json_encode($req);
  81 + return $this->post($url, $body);
  82 + }
  83 +
  84 + /**
  85 + * @param array $domains 待获取带宽数据的域名数组
  86 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  87 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  88 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  89 + *
  90 + * @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
  91 + *
  92 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  93 + */
  94 + public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
  95 + {
  96 + $req = array();
  97 + $req['domains'] = implode(';', $domains);
  98 + $req['startDate'] = $startDate;
  99 + $req['endDate'] = $endDate;
  100 + $req['granularity'] = $granularity;
  101 +
  102 + $url = $this->server . '/v2/tune/bandwidth';
  103 + $body = json_encode($req);
  104 + return $this->post($url, $body);
  105 + }
  106 +
  107 + /**
  108 + * @param array $domains 待获取流量数据的域名数组
  109 + * @param string $startDate 开始的日期,格式类似 2017-01-01
  110 + * @param string $endDate 结束的日期,格式类似 2017-01-01
  111 + * @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
  112 + *
  113 + * @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
  114 + *
  115 + * @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
  116 + */
  117 + public function getFluxData(array $domains, $startDate, $endDate, $granularity)
  118 + {
  119 + $req = array();
  120 + $req['domains'] = implode(';', $domains);
  121 + $req['startDate'] = $startDate;
  122 + $req['endDate'] = $endDate;
  123 + $req['granularity'] = $granularity;
  124 +
  125 + $url = $this->server . '/v2/tune/flux';
  126 + $body = json_encode($req);
  127 + return $this->post($url, $body);
  128 + }
  129 +
  130 + /**
  131 + * @param array $domains 待获取日志下载链接的域名数组
  132 + * @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
  133 + *
  134 + * @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
  135 + *
  136 + * @link http://developer.qiniu.com/article/fusion/api/log.html
  137 + */
  138 + public function getCdnLogList(array $domains, $logDate)
  139 + {
  140 + $req = array();
  141 + $req['domains'] = implode(';', $domains);
  142 + $req['day'] = $logDate;
  143 +
  144 + $url = $this->server . '/v2/tune/log/list';
  145 + $body = json_encode($req);
  146 + return $this->post($url, $body);
  147 + }
  148 +
  149 + private function post($url, $body)
  150 + {
  151 + $headers = $this->auth->authorization($url, $body, 'application/json');
  152 + $headers['Content-Type'] = 'application/json';
  153 + $ret = Client::post($url, $body, $headers);
  154 + if (!$ret->ok()) {
  155 + return array(null, new Error($url, $ret));
  156 + }
  157 + $r = ($ret->body === null) ? array() : $ret->json();
  158 + return array($r, null);
  159 + }
  160 +
  161 + /**
  162 + * 构建时间戳防盗链鉴权的访问外链
  163 + *
  164 + * @param string $rawUrl 需要签名的资源url
  165 + * @param string $encryptKey 时间戳防盗链密钥
  166 + * @param string $durationInSeconds 链接的有效期(以秒为单位)
  167 + *
  168 + * @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
  169 + */
  170 + public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
  171 + {
  172 + $parsedUrl = parse_url($rawUrl);
  173 + $deadline = time() + $durationInSeconds;
  174 + $expireHex = dechex($deadline);
  175 + $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
  176 + $strToSign = $encryptKey . $path . $expireHex;
  177 + $signStr = md5($strToSign);
  178 + if (isset($parsedUrl['query'])) {
  179 + $signedUrl = $rawUrl . '&sign=' . $signStr . '&t=' . $expireHex;
  180 + } else {
  181 + $signedUrl = $rawUrl . '?sign=' . $signStr . '&t=' . $expireHex;
  182 + }
  183 + return $signedUrl;
  184 + }
  185 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +final class Config
  5 +{
  6 + const SDK_VER = '7.2.10';
  7 +
  8 + const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
  9 +
  10 + const RSF_HOST = 'rsf.qiniu.com';
  11 + const API_HOST = 'api.qiniu.com';
  12 + const RS_HOST = 'rs.qiniu.com'; //RS Host
  13 + const UC_HOST = 'uc.qbox.me'; //UC Host
  14 + const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
  15 + const ARGUS_HOST = 'argus.atlab.ai';
  16 + const CASTER_HOST = 'pili-caster.qiniuapi.com';
  17 + const SMS_HOST="https://sms.qiniuapi.com";
  18 + const RTCAPI_VERSION = 'v3';
  19 + const SMS_VERSION='v1';
  20 +
  21 + // Zone 空间对应的存储区域
  22 + public $region;
  23 + //BOOL 是否使用https域名
  24 + public $useHTTPS;
  25 + //BOOL 是否使用CDN加速上传域名
  26 + public $useCdnDomains;
  27 + // Zone Cache
  28 + private $regionCache;
  29 +
  30 + // 构造函数
  31 + public function __construct(Region $z = null)
  32 + {
  33 + $this->zone = $z;
  34 + $this->useHTTPS = false;
  35 + $this->useCdnDomains = false;
  36 + $this->regionCache = array();
  37 + }
  38 +
  39 + public function getUpHost($accessKey, $bucket)
  40 + {
  41 + $region = $this->getRegion($accessKey, $bucket);
  42 + if ($this->useHTTPS === true) {
  43 + $scheme = "https://";
  44 + } else {
  45 + $scheme = "http://";
  46 + }
  47 +
  48 + $host = $region->srcUpHosts[0];
  49 + if ($this->useCdnDomains === true) {
  50 + $host = $region->cdnUpHosts[0];
  51 + }
  52 +
  53 + return $scheme . $host;
  54 + }
  55 +
  56 + public function getUpBackupHost($accessKey, $bucket)
  57 + {
  58 + $region = $this->getRegion($accessKey, $bucket);
  59 + if ($this->useHTTPS === true) {
  60 + $scheme = "https://";
  61 + } else {
  62 + $scheme = "http://";
  63 + }
  64 +
  65 + $host = $region->cdnUpHosts[0];
  66 + if ($this->useCdnDomains === true) {
  67 + $host = $region->srcUpHosts[0];
  68 + }
  69 +
  70 + return $scheme . $host;
  71 + }
  72 +
  73 + public function getRsHost($accessKey, $bucket)
  74 + {
  75 + $region = $this->getRegion($accessKey, $bucket);
  76 +
  77 + if ($this->useHTTPS === true) {
  78 + $scheme = "https://";
  79 + } else {
  80 + $scheme = "http://";
  81 + }
  82 +
  83 + return $scheme . $region->rsHost;
  84 + }
  85 +
  86 + public function getRsfHost($accessKey, $bucket)
  87 + {
  88 + $region = $this->getRegion($accessKey, $bucket);
  89 +
  90 + if ($this->useHTTPS === true) {
  91 + $scheme = "https://";
  92 + } else {
  93 + $scheme = "http://";
  94 + }
  95 +
  96 + return $scheme . $region->rsfHost;
  97 + }
  98 +
  99 + public function getIovipHost($accessKey, $bucket)
  100 + {
  101 + $region = $this->getRegion($accessKey, $bucket);
  102 +
  103 + if ($this->useHTTPS === true) {
  104 + $scheme = "https://";
  105 + } else {
  106 + $scheme = "http://";
  107 + }
  108 +
  109 + return $scheme . $region->iovipHost;
  110 + }
  111 +
  112 + public function getApiHost($accessKey, $bucket)
  113 + {
  114 + $region = $this->getRegion($accessKey, $bucket);
  115 +
  116 + if ($this->useHTTPS === true) {
  117 + $scheme = "https://";
  118 + } else {
  119 + $scheme = "http://";
  120 + }
  121 +
  122 + return $scheme . $region->apiHost;
  123 + }
  124 +
  125 + private function getRegion($accessKey, $bucket)
  126 + {
  127 + $cacheId = "$accessKey:$bucket";
  128 +
  129 + if (isset($this->regionCache[$cacheId])) {
  130 + $region = $this->regionCache[$cacheId];
  131 + } elseif (isset($this->zone)) {
  132 + $region = $this->zone;
  133 + $this->regionCache[$cacheId] = $region;
  134 + } else {
  135 + $region = Zone::queryZone($accessKey, $bucket);
  136 + $this->regionCache[$cacheId] = $region;
  137 + }
  138 + return $region;
  139 + }
  140 +}
  1 +<?php
  2 +
  3 +namespace Qiniu;
  4 +
  5 +use Qiniu\Config;
  6 +
  7 +final class Etag
  8 +{
  9 + private static function packArray($v, $a)
  10 + {
  11 + return call_user_func_array('pack', array_merge(array($v), (array)$a));
  12 + }
  13 +
  14 + private static function blockCount($fsize)
  15 + {
  16 + return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
  17 + }
  18 +
  19 + private static function calcSha1($data)
  20 + {
  21 + $sha1Str = sha1($data, true);
  22 + $err = error_get_last();
  23 + if ($err !== null) {
  24 + return array(null, $err);
  25 + }
  26 + $byteArray = unpack('C*', $sha1Str);
  27 + return array($byteArray, null);
  28 + }
  29 +
  30 +
  31 + public static function sum($filename)
  32 + {
  33 + $fhandler = fopen($filename, 'r');
  34 + $err = error_get_last();
  35 + if ($err !== null) {
  36 + return array(null, $err);
  37 + }
  38 +
  39 + $fstat = fstat($fhandler);
  40 + $fsize = $fstat['size'];
  41 + if ((int)$fsize === 0) {
  42 + fclose($fhandler);
  43 + return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
  44 + }
  45 + $blockCnt = self::blockCount($fsize);
  46 + $sha1Buf = array();
  47 +
  48 + if ($blockCnt <= 1) {
  49 + array_push($sha1Buf, 0x16);
  50 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  51 + if ($err !== null) {
  52 + fclose($fhandler);
  53 + return array(null, $err);
  54 + }
  55 + list($sha1Code,) = self::calcSha1($fdata);
  56 + $sha1Buf = array_merge($sha1Buf, $sha1Code);
  57 + } else {
  58 + array_push($sha1Buf, 0x96);
  59 + $sha1BlockBuf = array();
  60 + for ($i = 0; $i < $blockCnt; $i++) {
  61 + $fdata = fread($fhandler, Config::BLOCK_SIZE);
  62 + list($sha1Code, $err) = self::calcSha1($fdata);
  63 + if ($err !== null) {
  64 + fclose($fhandler);
  65 + return array(null, $err);
  66 + }
  67 + $sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
  68 + }
  69 + $tmpData = self::packArray('C*', $sha1BlockBuf);
  70 + list($sha1Final,) = self::calcSha1($tmpData);
  71 + $sha1Buf = array_merge($sha1Buf, $sha1Final);
  72 + }
  73 + $etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
  74 + return array($etag, null);
  75 + }
  76 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +use Qiniu\Config;
  5 +use Qiniu\Http\Request;
  6 +use Qiniu\Http\Response;
  7 +
  8 +final class Client
  9 +{
  10 + public static function get($url, array $headers = array())
  11 + {
  12 + $request = new Request('GET', $url, $headers);
  13 + return self::sendRequest($request);
  14 + }
  15 +
  16 + public static function delete($url, array $headers = array())
  17 + {
  18 + $request = new Request('DELETE', $url, $headers);
  19 + return self::sendRequest($request);
  20 + }
  21 +
  22 + public static function post($url, $body, array $headers = array())
  23 + {
  24 + $request = new Request('POST', $url, $headers, $body);
  25 + return self::sendRequest($request);
  26 + }
  27 +
  28 + public static function PUT($url, $body, array $headers = array())
  29 + {
  30 + $request = new Request('PUT', $url, $headers, $body);
  31 + return self::sendRequest($request);
  32 + }
  33 +
  34 + public static function multipartPost(
  35 + $url,
  36 + $fields,
  37 + $name,
  38 + $fileName,
  39 + $fileBody,
  40 + $mimeType = null,
  41 + array $headers = array()
  42 + ) {
  43 + $data = array();
  44 + $mimeBoundary = md5(microtime());
  45 +
  46 + foreach ($fields as $key => $val) {
  47 + array_push($data, '--' . $mimeBoundary);
  48 + array_push($data, "Content-Disposition: form-data; name=\"$key\"");
  49 + array_push($data, '');
  50 + array_push($data, $val);
  51 + }
  52 +
  53 + array_push($data, '--' . $mimeBoundary);
  54 + $finalMimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType;
  55 + $finalFileName = self::escapeQuotes($fileName);
  56 + array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$finalFileName\"");
  57 + array_push($data, "Content-Type: $finalMimeType");
  58 + array_push($data, '');
  59 + array_push($data, $fileBody);
  60 +
  61 + array_push($data, '--' . $mimeBoundary . '--');
  62 + array_push($data, '');
  63 +
  64 + $body = implode("\r\n", $data);
  65 + // var_dump($data);exit;
  66 + $contentType = 'multipart/form-data; boundary=' . $mimeBoundary;
  67 + $headers['Content-Type'] = $contentType;
  68 + $request = new Request('POST', $url, $headers, $body);
  69 + return self::sendRequest($request);
  70 + }
  71 +
  72 + private static function userAgent()
  73 + {
  74 + $sdkInfo = "QiniuPHP/" . Config::SDK_VER;
  75 +
  76 + $systemInfo = php_uname("s");
  77 + $machineInfo = php_uname("m");
  78 +
  79 + $envInfo = "($systemInfo/$machineInfo)";
  80 +
  81 + $phpVer = phpversion();
  82 +
  83 + $ua = "$sdkInfo $envInfo PHP/$phpVer";
  84 + return $ua;
  85 + }
  86 +
  87 + public static function sendRequest($request)
  88 + {
  89 + $t1 = microtime(true);
  90 + $ch = curl_init();
  91 + $options = array(
  92 + CURLOPT_USERAGENT => self::userAgent(),
  93 + CURLOPT_RETURNTRANSFER => true,
  94 + CURLOPT_SSL_VERIFYPEER => false,
  95 + CURLOPT_SSL_VERIFYHOST => false,
  96 + CURLOPT_HEADER => true,
  97 + CURLOPT_NOBODY => false,
  98 + CURLOPT_CUSTOMREQUEST => $request->method,
  99 + CURLOPT_URL => $request->url,
  100 + );
  101 + // Handle open_basedir & safe mode
  102 + if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
  103 + $options[CURLOPT_FOLLOWLOCATION] = true;
  104 + }
  105 + if (!empty($request->headers)) {
  106 + $headers = array();
  107 + foreach ($request->headers as $key => $val) {
  108 + array_push($headers, "$key: $val");
  109 + }
  110 + $options[CURLOPT_HTTPHEADER] = $headers;
  111 + }
  112 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
  113 + if (!empty($request->body)) {
  114 + $options[CURLOPT_POSTFIELDS] = $request->body;
  115 + }
  116 + curl_setopt_array($ch, $options);
  117 + $result = curl_exec($ch);
  118 + $t2 = microtime(true);
  119 + $duration = round($t2 - $t1, 3);
  120 + $ret = curl_errno($ch);
  121 + if ($ret !== 0) {
  122 + $r = new Response(-1, $duration, array(), null, curl_error($ch));
  123 + curl_close($ch);
  124 + return $r;
  125 + }
  126 + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  127 + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
  128 + $headers = self::parseHeaders(substr($result, 0, $header_size));
  129 + $body = substr($result, $header_size);
  130 + curl_close($ch);
  131 + return new Response($code, $duration, $headers, $body, null);
  132 + }
  133 +
  134 + private static function parseHeaders($raw)
  135 + {
  136 + $headers = array();
  137 + $headerLines = explode("\r\n", $raw);
  138 + foreach ($headerLines as $line) {
  139 + $headerLine = trim($line);
  140 + $kv = explode(':', $headerLine);
  141 + if (count($kv) > 1) {
  142 + $kv[0] =self::ucwordsHyphen($kv[0]);
  143 + $headers[$kv[0]] = trim($kv[1]);
  144 + }
  145 + }
  146 + return $headers;
  147 + }
  148 +
  149 + private static function escapeQuotes($str)
  150 + {
  151 + $find = array("\\", "\"");
  152 + $replace = array("\\\\", "\\\"");
  153 + return str_replace($find, $replace, $str);
  154 + }
  155 +
  156 + private static function ucwordsHyphen($str)
  157 + {
  158 + return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
  159 + }
  160 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +/**
  5 + * 七牛业务请求逻辑错误封装类,主要用来解析API请求返回如下的内容:
  6 + * <pre>
  7 + * {"error" : "detailed error message"}
  8 + * </pre>
  9 + */
  10 +final class Error
  11 +{
  12 + private $url;
  13 + private $response;
  14 +
  15 + public function __construct($url, $response)
  16 + {
  17 + $this->url = $url;
  18 + $this->response = $response;
  19 + }
  20 +
  21 + public function code()
  22 + {
  23 + return $this->response->statusCode;
  24 + }
  25 +
  26 + public function getResponse()
  27 + {
  28 + return $this->response;
  29 + }
  30 +
  31 + public function message()
  32 + {
  33 + return $this->response->error;
  34 + }
  35 +}
  1 +<?php
  2 +namespace Qiniu\Http;
  3 +
  4 +final class Request
  5 +{
  6 + public $url;
  7 + public $headers;
  8 + public $body;
  9 + public $method;
  10 +
  11 + public function __construct($method, $url, array $headers = array(), $body = null)
  12 + {
  13 + $this->method = strtoupper($method);
  14 + $this->url = $url;
  15 + $this->headers = $headers;
  16 + $this->body = $body;
  17 + }
  18 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Http;
  4 +
  5 +/**
  6 + * HTTP response Object
  7 + */
  8 +final class Response
  9 +{
  10 + public $statusCode;
  11 + public $headers;
  12 + public $body;
  13 + public $error;
  14 + private $jsonData;
  15 + public $duration;
  16 +
  17 + /** @var array Mapping of status codes to reason phrases */
  18 + private static $statusTexts = array(
  19 + 100 => 'Continue',
  20 + 101 => 'Switching Protocols',
  21 + 102 => 'Processing',
  22 + 200 => 'OK',
  23 + 201 => 'Created',
  24 + 202 => 'Accepted',
  25 + 203 => 'Non-Authoritative Information',
  26 + 204 => 'No Content',
  27 + 205 => 'Reset Content',
  28 + 206 => 'Partial Content',
  29 + 207 => 'Multi-Status',
  30 + 208 => 'Already Reported',
  31 + 226 => 'IM Used',
  32 + 300 => 'Multiple Choices',
  33 + 301 => 'Moved Permanently',
  34 + 302 => 'Found',
  35 + 303 => 'See Other',
  36 + 304 => 'Not Modified',
  37 + 305 => 'Use Proxy',
  38 + 307 => 'Temporary Redirect',
  39 + 308 => 'Permanent Redirect',
  40 + 400 => 'Bad Request',
  41 + 401 => 'Unauthorized',
  42 + 402 => 'Payment Required',
  43 + 403 => 'Forbidden',
  44 + 404 => 'Not Found',
  45 + 405 => 'Method Not Allowed',
  46 + 406 => 'Not Acceptable',
  47 + 407 => 'Proxy Authentication Required',
  48 + 408 => 'Request Timeout',
  49 + 409 => 'Conflict',
  50 + 410 => 'Gone',
  51 + 411 => 'Length Required',
  52 + 412 => 'Precondition Failed',
  53 + 413 => 'Request Entity Too Large',
  54 + 414 => 'Request-URI Too Long',
  55 + 415 => 'Unsupported Media Type',
  56 + 416 => 'Requested Range Not Satisfiable',
  57 + 417 => 'Expectation Failed',
  58 + 422 => 'Unprocessable Entity',
  59 + 423 => 'Locked',
  60 + 424 => 'Failed Dependency',
  61 + 425 => 'Reserved for WebDAV advanced collections expired proposal',
  62 + 426 => 'Upgrade required',
  63 + 428 => 'Precondition Required',
  64 + 429 => 'Too Many Requests',
  65 + 431 => 'Request Header Fields Too Large',
  66 + 500 => 'Internal Server Error',
  67 + 501 => 'Not Implemented',
  68 + 502 => 'Bad Gateway',
  69 + 503 => 'Service Unavailable',
  70 + 504 => 'Gateway Timeout',
  71 + 505 => 'HTTP Version Not Supported',
  72 + 506 => 'Variant Also Negotiates (Experimental)',
  73 + 507 => 'Insufficient Storage',
  74 + 508 => 'Loop Detected',
  75 + 510 => 'Not Extended',
  76 + 511 => 'Network Authentication Required',
  77 + );
  78 +
  79 + /**
  80 + * @param int $code 状态码
  81 + * @param double $duration 请求时长
  82 + * @param array $headers 响应头部
  83 + * @param string $body 响应内容
  84 + * @param string $error 错误描述
  85 + */
  86 + public function __construct($code, $duration, array $headers = array(), $body = null, $error = null)
  87 + {
  88 + $this->statusCode = $code;
  89 + $this->duration = $duration;
  90 + $this->headers = $headers;
  91 + $this->body = $body;
  92 + $this->error = $error;
  93 + $this->jsonData = null;
  94 + if ($error !== null) {
  95 + return;
  96 + }
  97 +
  98 + if ($body === null) {
  99 + if ($code >= 400) {
  100 + $this->error = self::$statusTexts[$code];
  101 + }
  102 + return;
  103 + }
  104 + if (self::isJson($headers)) {
  105 + try {
  106 + $jsonData = self::bodyJson($body);
  107 + if ($code >= 400) {
  108 + $this->error = $body;
  109 + if ($jsonData['error'] !== null) {
  110 + $this->error = $jsonData['error'];
  111 + }
  112 + }
  113 + $this->jsonData = $jsonData;
  114 + } catch (\InvalidArgumentException $e) {
  115 + $this->error = $body;
  116 + if ($code >= 200 && $code < 300) {
  117 + $this->error = $e->getMessage();
  118 + }
  119 + }
  120 + } elseif ($code >= 400) {
  121 + $this->error = $body;
  122 + }
  123 + return;
  124 + }
  125 +
  126 + public function json()
  127 + {
  128 + return $this->jsonData;
  129 + }
  130 +
  131 + private static function bodyJson($body)
  132 + {
  133 + return \Qiniu\json_decode((string) $body, true, 512);
  134 + }
  135 +
  136 + public function xVia()
  137 + {
  138 + $via = $this->headers['X-Via'];
  139 + if ($via === null) {
  140 + $via = $this->headers['X-Px'];
  141 + }
  142 + if ($via === null) {
  143 + $via = $this->headers['Fw-Via'];
  144 + }
  145 + return $via;
  146 + }
  147 +
  148 + public function xLog()
  149 + {
  150 + return $this->headers['X-Log'];
  151 + }
  152 +
  153 + public function xReqId()
  154 + {
  155 + return $this->headers['X-Reqid'];
  156 + }
  157 +
  158 + public function ok()
  159 + {
  160 + return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
  161 + }
  162 +
  163 + public function needRetry()
  164 + {
  165 + $code = $this->statusCode;
  166 + if ($code < 0 || ($code / 100 === 5 and $code !== 579) || $code === 996) {
  167 + return true;
  168 + }
  169 + }
  170 +
  171 + private static function isJson($headers)
  172 + {
  173 + return array_key_exists('Content-Type', $headers) &&
  174 + strpos($headers['Content-Type'], 'application/json') === 0;
  175 + }
  176 +}
  1 +<?php
  2 +namespace Qiniu\Processing;
  3 +
  4 +use Qiniu;
  5 +
  6 +/**
  7 + * 主要涉及图片链接拼接
  8 + *
  9 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  10 + */
  11 +final class ImageUrlBuilder
  12 +{
  13 + /**
  14 + * mode合法范围值
  15 + *
  16 + * @var array
  17 + */
  18 + protected $modeArr = array(0, 1, 2, 3, 4, 5);
  19 +
  20 + /**
  21 + * format合法值
  22 + *
  23 + * @var array
  24 + */
  25 + protected $formatArr = array('psd', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'bmp');
  26 +
  27 + /**
  28 + * 水印图片位置合法值
  29 + *
  30 + * @var array
  31 + */
  32 + protected $gravityArr = array('NorthWest', 'North', 'NorthEast',
  33 + 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast');
  34 +
  35 + /**
  36 + * 缩略图链接拼接
  37 + *
  38 + * @param string $url 图片链接
  39 + * @param int $mode 缩略模式
  40 + * @param int $width 宽度
  41 + * @param int $height 长度
  42 + * @param string $format 输出类型
  43 + * @param int $quality 图片质量
  44 + * @param int $interlace 是否支持渐进显示
  45 + * @param int $ignoreError 忽略结果
  46 + * @return string
  47 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  48 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  49 + */
  50 + public function thumbnail(
  51 + $url,
  52 + $mode,
  53 + $width,
  54 + $height,
  55 + $format = null,
  56 + $interlace = null,
  57 + $quality = null,
  58 + $ignoreError = 1
  59 + ) {
  60 +
  61 + // url合法效验
  62 + if (!$this->isUrl($url)) {
  63 + return $url;
  64 + }
  65 +
  66 + // 参数合法性效验
  67 + if (!in_array(intval($mode), $this->modeArr, true)) {
  68 + return $url;
  69 + }
  70 +
  71 + if (!$width || !$height) {
  72 + return $url;
  73 + }
  74 +
  75 + $thumbStr = 'imageView2/' . $mode . '/w/' . $width . '/h/' . $height . '/';
  76 +
  77 + // 拼接输出格式
  78 + if (!is_null($format)
  79 + && in_array($format, $this->formatArr)
  80 + ) {
  81 + $thumbStr .= 'format/' . $format . '/';
  82 + }
  83 +
  84 + // 拼接渐进显示
  85 + if (!is_null($interlace)
  86 + && in_array(intval($interlace), array(0, 1), true)
  87 + ) {
  88 + $thumbStr .= 'interlace/' . $interlace . '/';
  89 + }
  90 +
  91 + // 拼接图片质量
  92 + if (!is_null($quality)
  93 + && intval($quality) >= 0
  94 + && intval($quality) <= 100
  95 + ) {
  96 + $thumbStr .= 'q/' . $quality . '/';
  97 + }
  98 +
  99 + $thumbStr .= 'ignore-error/' . $ignoreError . '/';
  100 +
  101 + // 如果有query_string用|线分割实现多参数
  102 + return $url . ($this->hasQuery($url) ? '|' : '?') . $thumbStr;
  103 + }
  104 +
  105 + /**
  106 + * 图片水印
  107 + *
  108 + * @param string $url 图片链接
  109 + * @param string $image 水印图片链接
  110 + * @param numeric $dissolve 透明度
  111 + * @param string $gravity 水印位置
  112 + * @param numeric $dx 横轴边距
  113 + * @param numeric $dy 纵轴边距
  114 + * @param numeric $watermarkScale 自适应原图的短边比例
  115 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
  116 + * @return string
  117 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  118 + */
  119 + public function waterImg(
  120 + $url,
  121 + $image,
  122 + $dissolve = 100,
  123 + $gravity = 'SouthEast',
  124 + $dx = null,
  125 + $dy = null,
  126 + $watermarkScale = null
  127 + ) {
  128 + // url合法效验
  129 + if (!$this->isUrl($url)) {
  130 + return $url;
  131 + }
  132 +
  133 + $waterStr = 'watermark/1/image/' . \Qiniu\base64_urlSafeEncode($image) . '/';
  134 +
  135 + // 拼接水印透明度
  136 + if (is_numeric($dissolve)
  137 + && $dissolve <= 100
  138 + ) {
  139 + $waterStr .= 'dissolve/' . $dissolve . '/';
  140 + }
  141 +
  142 + // 拼接水印位置
  143 + if (in_array($gravity, $this->gravityArr, true)) {
  144 + $waterStr .= 'gravity/' . $gravity . '/';
  145 + }
  146 +
  147 + // 拼接横轴边距
  148 + if (!is_null($dx)
  149 + && is_numeric($dx)
  150 + ) {
  151 + $waterStr .= 'dx/' . $dx . '/';
  152 + }
  153 +
  154 + // 拼接纵轴边距
  155 + if (!is_null($dy)
  156 + && is_numeric($dy)
  157 + ) {
  158 + $waterStr .= 'dy/' . $dy . '/';
  159 + }
  160 +
  161 + // 拼接自适应原图的短边比例
  162 + if (!is_null($watermarkScale)
  163 + && is_numeric($watermarkScale)
  164 + && $watermarkScale > 0
  165 + && $watermarkScale < 1
  166 + ) {
  167 + $waterStr .= 'ws/' . $watermarkScale . '/';
  168 + }
  169 +
  170 + // 如果有query_string用|线分割实现多参数
  171 + return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
  172 + }
  173 +
  174 + /**
  175 + * 文字水印
  176 + *
  177 + * @param string $url 图片链接
  178 + * @param string $text 文字
  179 + * @param string $font 文字字体
  180 + * @param string $fontSize 文字字号
  181 + * @param string $fontColor 文字颜色
  182 + * @param numeric $dissolve 透明度
  183 + * @param string $gravity 水印位置
  184 + * @param numeric $dx 横轴边距
  185 + * @param numeric $dy 纵轴边距
  186 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
  187 + * @return string
  188 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  189 + */
  190 + public function waterText(
  191 + $url,
  192 + $text,
  193 + $font = '黑体',
  194 + $fontSize = 0,
  195 + $fontColor = null,
  196 + $dissolve = 100,
  197 + $gravity = 'SouthEast',
  198 + $dx = null,
  199 + $dy = null
  200 + ) {
  201 + // url合法效验
  202 + if (!$this->isUrl($url)) {
  203 + return $url;
  204 + }
  205 +
  206 + $waterStr = 'watermark/2/text/'
  207 + . \Qiniu\base64_urlSafeEncode($text) . '/font/'
  208 + . \Qiniu\base64_urlSafeEncode($font) . '/';
  209 +
  210 + // 拼接文字大小
  211 + if (is_int($fontSize)) {
  212 + $waterStr .= 'fontsize/' . $fontSize . '/';
  213 + }
  214 +
  215 + // 拼接文字颜色
  216 + if (!is_null($fontColor)
  217 + && $fontColor
  218 + ) {
  219 + $waterStr .= 'fill/' . \Qiniu\base64_urlSafeEncode($fontColor) . '/';
  220 + }
  221 +
  222 + // 拼接水印透明度
  223 + if (is_numeric($dissolve)
  224 + && $dissolve <= 100
  225 + ) {
  226 + $waterStr .= 'dissolve/' . $dissolve . '/';
  227 + }
  228 +
  229 + // 拼接水印位置
  230 + if (in_array($gravity, $this->gravityArr, true)) {
  231 + $waterStr .= 'gravity/' . $gravity . '/';
  232 + }
  233 +
  234 + // 拼接横轴边距
  235 + if (!is_null($dx)
  236 + && is_numeric($dx)
  237 + ) {
  238 + $waterStr .= 'dx/' . $dx . '/';
  239 + }
  240 +
  241 + // 拼接纵轴边距
  242 + if (!is_null($dy)
  243 + && is_numeric($dy)
  244 + ) {
  245 + $waterStr .= 'dy/' . $dy . '/';
  246 + }
  247 +
  248 + // 如果有query_string用|线分割实现多参数
  249 + return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
  250 + }
  251 +
  252 + /**
  253 + * 效验url合法性
  254 + *
  255 + * @param string $url url链接
  256 + * @return string
  257 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  258 + */
  259 + protected function isUrl($url)
  260 + {
  261 + $urlArr = parse_url($url);
  262 +
  263 + return $urlArr['scheme']
  264 + && in_array($urlArr['scheme'], array('http', 'https'))
  265 + && $urlArr['host']
  266 + && $urlArr['path'];
  267 + }
  268 +
  269 + /**
  270 + * 检测是否有query
  271 + *
  272 + * @param string $url url链接
  273 + * @return string
  274 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  275 + */
  276 + protected function hasQuery($url)
  277 + {
  278 + $urlArr = parse_url($url);
  279 +
  280 + return !empty($urlArr['query']);
  281 + }
  282 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Processing;
  4 +
  5 +use Qiniu\Http\Client;
  6 +use Qiniu\Http\Error;
  7 +
  8 +final class Operation
  9 +{
  10 +
  11 + private $auth;
  12 + private $token_expire;
  13 + private $domain;
  14 +
  15 + public function __construct($domain, $auth = null, $token_expire = 3600)
  16 + {
  17 + $this->auth = $auth;
  18 + $this->domain = $domain;
  19 + $this->token_expire = $token_expire;
  20 + }
  21 +
  22 +
  23 + /**
  24 + * 对资源文件进行处理
  25 + *
  26 + * @param $key 待处理的资源文件名
  27 + * @param $fops string|array fop操作,多次fop操作以array的形式传入。
  28 + * eg. imageView2/1/w/200/h/200, imageMogr2/thumbnail/!75px
  29 + *
  30 + * @return array 文件处理后的结果及错误。
  31 + *
  32 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
  33 + */
  34 + public function execute($key, $fops)
  35 + {
  36 + $url = $this->buildUrl($key, $fops);
  37 + $resp = Client::get($url);
  38 + if (!$resp->ok()) {
  39 + return array(null, new Error($url, $resp));
  40 + }
  41 + if ($resp->json() !== null) {
  42 + return array($resp->json(), null);
  43 + }
  44 + return array($resp->body, null);
  45 + }
  46 +
  47 + public function buildUrl($key, $fops, $protocol = 'http')
  48 + {
  49 + if (is_array($fops)) {
  50 + $fops = implode('|', $fops);
  51 + }
  52 +
  53 + $url = $protocol . "://$this->domain/$key?$fops";
  54 + if ($this->auth !== null) {
  55 + $url = $this->auth->privateDownloadUrl($url, $this->token_expire);
  56 + }
  57 +
  58 + return $url;
  59 + }
  60 +}
  1 +<?php
  2 +namespace Qiniu\Processing;
  3 +
  4 +use Qiniu\Config;
  5 +use Qiniu\Http\Client;
  6 +use Qiniu\Http\Error;
  7 +use Qiniu\Processing\Operation;
  8 +
  9 +/**
  10 + * 持久化处理类,该类用于主动触发异步持久化操作.
  11 + *
  12 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/pfop/pfop.html
  13 + */
  14 +final class PersistentFop
  15 +{
  16 + /**
  17 + * @var 账号管理密钥对,Auth对象
  18 + */
  19 + private $auth;
  20 +
  21 + /*
  22 + * @var 配置对象,Config 对象
  23 + * */
  24 + private $config;
  25 +
  26 +
  27 + public function __construct($auth, $config = null)
  28 + {
  29 + $this->auth = $auth;
  30 + if ($config == null) {
  31 + $this->config = new Config();
  32 + } else {
  33 + $this->config = $config;
  34 + }
  35 + }
  36 +
  37 + /**
  38 + * 对资源文件进行异步持久化处理
  39 + * @param $bucket 资源所在空间
  40 + * @param $key 待处理的源文件
  41 + * @param $fops string|array 待处理的pfop操作,多个pfop操作以array的形式传入。
  42 + * eg. avthumb/mp3/ab/192k, vframe/jpg/offset/7/w/480/h/360
  43 + * @param $pipeline 资源处理队列
  44 + * @param $notify_url 处理结果通知地址
  45 + * @param $force 是否强制执行一次新的指令
  46 + *
  47 + *
  48 + * @return array 返回持久化处理的persistentId, 和返回的错误。
  49 + *
  50 + * @link http://developer.qiniu.com/docs/v6/api/reference/fop/
  51 + */
  52 + public function execute($bucket, $key, $fops, $pipeline = null, $notify_url = null, $force = false)
  53 + {
  54 + if (is_array($fops)) {
  55 + $fops = implode(';', $fops);
  56 + }
  57 + $params = array('bucket' => $bucket, 'key' => $key, 'fops' => $fops);
  58 + \Qiniu\setWithoutEmpty($params, 'pipeline', $pipeline);
  59 + \Qiniu\setWithoutEmpty($params, 'notifyURL', $notify_url);
  60 + if ($force) {
  61 + $params['force'] = 1;
  62 + }
  63 + $data = http_build_query($params);
  64 + $scheme = "http://";
  65 + if ($this->config->useHTTPS === true) {
  66 + $scheme = "https://";
  67 + }
  68 + $url = $scheme . Config::API_HOST . '/pfop/';
  69 + $headers = $this->auth->authorization($url, $data, 'application/x-www-form-urlencoded');
  70 + $headers['Content-Type'] = 'application/x-www-form-urlencoded';
  71 + $response = Client::post($url, $data, $headers);
  72 + if (!$response->ok()) {
  73 + return array(null, new Error($url, $response));
  74 + }
  75 + $r = $response->json();
  76 + $id = $r['persistentId'];
  77 + return array($id, null);
  78 + }
  79 +
  80 + public function status($id)
  81 + {
  82 + $scheme = "http://";
  83 +
  84 + if ($this->config->useHTTPS === true) {
  85 + $scheme = "https://";
  86 + }
  87 + $url = $scheme . Config::API_HOST . "/status/get/prefop?id=$id";
  88 + $response = Client::get($url);
  89 + if (!$response->ok()) {
  90 + return array(null, new Error($url, $response));
  91 + }
  92 + return array($response->json(), null);
  93 + }
  94 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Http\Client;
  5 +use Qiniu\Http\Error;
  6 +
  7 +class Region
  8 +{
  9 +
  10 + //源站上传域名
  11 + public $srcUpHosts;
  12 + //CDN加速上传域名
  13 + public $cdnUpHosts;
  14 + //资源管理域名
  15 + public $rsHost;
  16 + //资源列举域名
  17 + public $rsfHost;
  18 + //资源处理域名
  19 + public $apiHost;
  20 + //IOVIP域名
  21 + public $iovipHost;
  22 +
  23 + //构造一个Region对象
  24 + public function __construct(
  25 + $srcUpHosts = array(),
  26 + $cdnUpHosts = array(),
  27 + $rsHost = "rs.qiniu.com",
  28 + $rsfHost = "rsf.qiniu.com",
  29 + $apiHost = "api.qiniu.com",
  30 + $iovipHost = null
  31 + ) {
  32 +
  33 + $this->srcUpHosts = $srcUpHosts;
  34 + $this->cdnUpHosts = $cdnUpHosts;
  35 + $this->rsHost = $rsHost;
  36 + $this->rsfHost = $rsfHost;
  37 + $this->apiHost = $apiHost;
  38 + $this->iovipHost = $iovipHost;
  39 + }
  40 +
  41 + //华东机房
  42 + public static function regionHuadong()
  43 + {
  44 + $regionHuadong = new Region(
  45 + array("up.qiniup.com", 'up-jjh.qiniup.com', 'up-xs.qiniup.com'),
  46 + array('upload.qiniup.com', 'upload-jjh.qiniup.com', 'upload-xs.qiniup.com'),
  47 + 'rs.qbox.me',
  48 + 'rsf.qbox.me',
  49 + 'api.qiniu.com',
  50 + 'iovip.qbox.me'
  51 + );
  52 + return $regionHuadong;
  53 + }
  54 +
  55 + //华东机房内网上传
  56 + public static function qvmRegionHuadong()
  57 + {
  58 + $qvmRegionHuadong = new Region(
  59 + array("free-qvm-z0-xs.qiniup.com"),
  60 + 'rs.qbox.me',
  61 + 'rsf.qbox.me',
  62 + 'api.qiniu.com',
  63 + 'iovip.qbox.me'
  64 + );
  65 + return $qvmRegionHuadong;
  66 + }
  67 +
  68 + //华北机房内网上传
  69 + public static function qvmRegionHuabei()
  70 + {
  71 + $qvmRegionHuabei = new Region(
  72 + array("free-qvm-z1-zz.qiniup.com"),
  73 + "rs-z1.qbox.me",
  74 + "rsf-z1.qbox.me",
  75 + "api-z1.qiniu.com",
  76 + "iovip-z1.qbox.me"
  77 + );
  78 + return $qvmRegionHuabei;
  79 + }
  80 +
  81 + //华北机房
  82 + public static function regionHuabei()
  83 + {
  84 + $regionHuabei = new Region(
  85 + array('up-z1.qiniup.com'),
  86 + array('upload-z1.qiniup.com'),
  87 + "rs-z1.qbox.me",
  88 + "rsf-z1.qbox.me",
  89 + "api-z1.qiniu.com",
  90 + "iovip-z1.qbox.me"
  91 + );
  92 +
  93 + return $regionHuabei;
  94 + }
  95 +
  96 + //华南机房
  97 + public static function regionHuanan()
  98 + {
  99 + $regionHuanan = new Region(
  100 + array('up-z2.qiniup.com', 'up-dg.qiniup.com', 'up-fs.qiniup.com'),
  101 + array('upload-z2.qiniup.com', 'upload-dg.qiniup.com', 'upload-fs.qiniup.com'),
  102 + "rs-z2.qbox.me",
  103 + "rsf-z2.qbox.me",
  104 + "api-z2.qiniu.com",
  105 + "iovip-z2.qbox.me"
  106 + );
  107 + return $regionHuanan;
  108 + }
  109 +
  110 + //北美机房
  111 + public static function regionNorthAmerica()
  112 + {
  113 + //北美机房
  114 + $regionNorthAmerica = new Region(
  115 + array('up-na0.qiniup.com'),
  116 + array('upload-na0.qiniup.com'),
  117 + "rs-na0.qbox.me",
  118 + "rsf-na0.qbox.me",
  119 + "api-na0.qiniu.com",
  120 + "iovip-na0.qbox.me"
  121 + );
  122 + return $regionNorthAmerica;
  123 + }
  124 +
  125 + //新加坡机房
  126 + public static function regionSingapore()
  127 + {
  128 + //新加坡机房
  129 + $regionSingapore = new Region(
  130 + array('up-as0.qiniup.com'),
  131 + array('upload-as0.qiniup.com'),
  132 + "rs-as0.qbox.me",
  133 + "rsf-as0.qbox.me",
  134 + "api-as0.qiniu.com",
  135 + "iovip-as0.qbox.me"
  136 + );
  137 + return $regionSingapore;
  138 + }
  139 +
  140 + /*
  141 + * GET /v2/query?ak=<ak>&&bucket=<bucket>
  142 + **/
  143 + public static function queryRegion($ak, $bucket)
  144 + {
  145 + $Region = new Region();
  146 + $url = Config::API_HOST . '/v2/query' . "?ak=$ak&bucket=$bucket";
  147 + $ret = Client::Get($url);
  148 + if (!$ret->ok()) {
  149 + return array(null, new Error($url, $ret));
  150 + }
  151 + $r = ($ret->body === null) ? array() : $ret->json();
  152 + //parse Region;
  153 +
  154 + $iovipHost = $r['io']['src']['main'][0];
  155 + $Region->iovipHost = $iovipHost;
  156 + $accMain = $r['up']['acc']['main'][0];
  157 + array_push($Region->cdnUpHosts, $accMain);
  158 + if (isset($r['up']['acc']['backup'])) {
  159 + foreach ($r['up']['acc']['backup'] as $key => $value) {
  160 + array_push($Region->cdnUpHosts, $value);
  161 + }
  162 + }
  163 + $srcMain = $r['up']['src']['main'][0];
  164 + array_push($Region->srcUpHosts, $srcMain);
  165 + if (isset($r['up']['src']['backup'])) {
  166 + foreach ($r['up']['src']['backup'] as $key => $value) {
  167 + array_push($Region->srcUpHosts, $value);
  168 + }
  169 + }
  170 +
  171 + //set specific hosts
  172 + if (strstr($Region->iovipHost, "z1") !== false) {
  173 + $Region->rsHost = "rs-z1.qbox.me";
  174 + $Region->rsfHost = "rsf-z1.qbox.me";
  175 + $Region->apiHost = "api-z1.qiniu.com";
  176 + } elseif (strstr($Region->iovipHost, "z2") !== false) {
  177 + $Region->rsHost = "rs-z2.qbox.me";
  178 + $Region->rsfHost = "rsf-z2.qbox.me";
  179 + $Region->apiHost = "api-z2.qiniu.com";
  180 + } elseif (strstr($Region->iovipHost, "na0") !== false) {
  181 + $Region->rsHost = "rs-na0.qbox.me";
  182 + $Region->rsfHost = "rsf-na0.qbox.me";
  183 + $Region->apiHost = "api-na0.qiniu.com";
  184 + } elseif (strstr($Region->iovipHost, "as0") !== false) {
  185 + $Region->rsHost = "rs-as0.qbox.me";
  186 + $Region->rsfHost = "rsf-as0.qbox.me";
  187 + $Region->apiHost = "api-as0.qiniu.com";
  188 + } else {
  189 + $Region->rsHost = "rs.qbox.me";
  190 + $Region->rsfHost = "rsf.qbox.me";
  191 + $Region->apiHost = "api.qiniu.com";
  192 + }
  193 +
  194 + return $Region;
  195 + }
  196 +}
  1 +<?php
  2 +namespace Qiniu\Rtc;
  3 +
  4 +use Qiniu\Http\Client;
  5 +use Qiniu\Http\Error;
  6 +use Qiniu\Config;
  7 +use Qiniu\Auth;
  8 +
  9 +class AppClient
  10 +{
  11 + private $auth;
  12 + private $baseURL;
  13 +
  14 + public function __construct(Auth $auth)
  15 + {
  16 + $this->auth = $auth;
  17 +
  18 + $this->baseURL = sprintf("%s/%s/apps", Config::RTCAPI_HOST, Config::RTCAPI_VERSION);
  19 + }
  20 +
  21 + /*
  22 + * 创建应用
  23 + * hub: 直播空间名
  24 + * title: app 的名称 注意,Title 不是唯一标识,重复 create 动作将生成多个 app
  25 + * maxUsers:人数限制
  26 + * NoAutoKickUser: bool 类型,可选,禁止自动踢人(抢流)。默认为 false ,
  27 + 即同一个身份的 client (app/room/user) ,新的连麦请求可以成功,旧连接被关闭。
  28 + */
  29 + public function createApp($hub, $title, $maxUsers = null, $noAutoKickUser = null)
  30 + {
  31 + $params['hub'] = $hub;
  32 + $params['title'] = $title;
  33 + if (!empty($maxUsers)) {
  34 + $params['maxUsers'] = $maxUsers;
  35 + }
  36 + if ($noAutoKickUser !== null) {
  37 + $params['noAutoKickUser'] = $noAutoKickUser;
  38 + }
  39 + $body = json_encode($params);
  40 + $ret = $this->post($this->baseURL, $body);
  41 + return $ret;
  42 + }
  43 +
  44 + /*
  45 + * 更新应用
  46 + * appId: app 的唯一标识,创建的时候由系统生成。
  47 + * Title: app 的名称, 可选。
  48 + * Hub: 绑定的直播 hub,可选,用于合流后 rtmp 推流。
  49 + * MaxUsers: int 类型,可选,连麦房间支持的最大在线人数。
  50 + * NoAutoKickUser: bool 类型,可选,禁止自动踢人。
  51 + * MergePublishRtmp: 连麦合流转推 RTMP 的配置,可选择。其详细配置包括如下
  52 + Enable: 布尔类型,用于开启和关闭所有房间的合流功能。
  53 + AudioOnly: 布尔类型,可选,指定是否只合成音频。
  54 + Height, Width: int64,可选,指定合流输出的高和宽,默认为 640 x 480。
  55 + OutputFps: int64,可选,指定合流输出的帧率,默认为 25 fps 。
  56 + OutputKbps: int64,可选,指定合流输出的码率,默认为 1000 。
  57 + URL: 合流后转推旁路直播的地址,可选,支持魔法变量配置按照连麦房间号生成不同的推流地址。如果是转推到七牛直播云,不建议使用该配置。
  58 + StreamTitle: 转推七牛直播云的流名,可选,支持魔法变量配置按照连麦房间号生成不同的流名。例如,配置 Hub 为 qn-zhibo ,配置 StreamTitle 为 $(roomName) ,则房间 meeting-001 的合流将会被转推到 rtmp://pili-publish.qn-zhibo.***.com/qn-zhibo/meeting-001地址。详细配置细则,请咨询七牛技术支持。
  59 + */
  60 + public function updateApp($appId, $hub, $title, $maxUsers = null, $mergePublishRtmp = null, $noAutoKickUser = null)
  61 + {
  62 + $url = $this->baseURL . '/' . $appId;
  63 + $params['hub'] = $hub;
  64 + $params['title'] = $title;
  65 + if (!empty($maxUsers)) {
  66 + $params['maxUsers'] = $maxUsers;
  67 + }
  68 + if ($noAutoKickUser !== null) {
  69 + $params['noAutoKickUser'] = $noAutoKickUser;
  70 + }
  71 + if (!empty($mergePublishRtmp)) {
  72 + $params['mergePublishRtmp'] = $mergePublishRtmp;
  73 + }
  74 + $body = json_encode($params);
  75 + $ret = $this->post($url, $body);
  76 + return $ret;
  77 + }
  78 +
  79 + /*
  80 + * 获取应用信息
  81 + * appId: app 的唯一标识,创建的时候由系统生成。
  82 + */
  83 + public function getApp($appId)
  84 + {
  85 + $url = $this->baseURL . '/' . $appId;
  86 + $ret = $this->get($url);
  87 + return $ret;
  88 + }
  89 +
  90 + /*
  91 + * 删除应用
  92 + * appId: app 的唯一标识,创建的时候由系统生成
  93 + */
  94 + public function deleteApp($appId)
  95 + {
  96 + $url = $this->baseURL . '/' . $appId;
  97 + list(, $err) = $this->delete($url);
  98 + return $err;
  99 + }
  100 +
  101 + /*
  102 + * 获取房间内用户列表
  103 + * appId: app 的唯一标识,创建的时候由系统生成。
  104 + * roomName: 操作所查询的连麦房间。
  105 + */
  106 + public function listUser($appId, $roomName)
  107 + {
  108 + $url = sprintf("%s/%s/rooms/%s/users", $this->baseURL, $appId, $roomName);
  109 + $ret = $this->get($url);
  110 + return $ret;
  111 + }
  112 +
  113 + /*
  114 + * 踢出用户
  115 + * appId: app 的唯一标识,创建的时候由系统生成。
  116 + * roomName: 连麦房间
  117 + * userId: 请求加入房间的用户ID
  118 + */
  119 + public function kickUser($appId, $roomName, $userId)
  120 + {
  121 + $url = sprintf("%s/%s/rooms/%s/users/%s", $this->baseURL, $appId, $roomName, $userId);
  122 + list(, $err) = $this->delete($url);
  123 + return $err;
  124 + }
  125 +
  126 + /*
  127 + * 获取应用中活跃房间
  128 + * appId: app 的唯一标识,创建的时候由系统生成。
  129 + * prefix: 所查询房间名的前缀索引,可以为空。
  130 + * offset: int 类型,分页查询的位移标记。
  131 + * limit: int 类型,此次查询的最大长度。
  132 + * GET /v3/apps/<AppID>/rooms?prefix=<RoomNamePrefix>&offset=<Offset>&limit=<Limit>
  133 + */
  134 + public function listActiveRooms($appId, $prefix = null, $offset = null, $limit = null)
  135 + {
  136 + if (isset($prefix)) {
  137 + $query['prefix'] = $prefix;
  138 + }
  139 + if (isset($offset)) {
  140 + $query['offset'] = $offset;
  141 + }
  142 + if (isset($limit)) {
  143 + $query['limit'] = $limit;
  144 + }
  145 + if (isset($query) && !empty($query)) {
  146 + $query = '?' . http_build_query($query);
  147 + $url = sprintf("%s/%s/rooms%s", $this->baseURL, $appId, $query);
  148 + } else {
  149 + $url = sprintf("%s/%s/rooms", $this->baseURL, $appId);
  150 + }
  151 + $ret = $this->get($url);
  152 + return $ret;
  153 + }
  154 +
  155 + /*
  156 + * 生成加入房间的令牌
  157 + * appId: app 的唯一标识,创建的时候由系统生成。
  158 + * roomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
  159 + * userId: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$
  160 + * expireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix
  161 + 绝对时间,token 将在该时间后失效。
  162 + * permission: 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。
  163 + 当权限角色为 "admin" 时,拥有将其他用户移除出房间等特权.
  164 + */
  165 + public function appToken($appId, $roomName, $userId, $expireAt, $permission)
  166 + {
  167 + $params['appId'] = $appId;
  168 + $params['userId'] = $userId;
  169 + $params['roomName'] = $roomName;
  170 + $params['permission'] = $permission;
  171 + $params['expireAt'] = $expireAt;
  172 + $appAccessString = json_encode($params);
  173 + return $this->auth->signWithData($appAccessString);
  174 + }
  175 +
  176 + private function get($url, $cType = null)
  177 + {
  178 + $rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
  179 + $rtcToken['Content-Type'] = $cType;
  180 + $ret = Client::get($url, $rtcToken);
  181 + if (!$ret->ok()) {
  182 + return array(null, new Error($url, $ret));
  183 + }
  184 + return array($ret->json(), null);
  185 + }
  186 +
  187 + private function delete($url, $contentType = 'application/json')
  188 + {
  189 + $rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
  190 + $rtcToken['Content-Type'] = $contentType;
  191 + $ret = Client::delete($url, $rtcToken);
  192 + if (!$ret->ok()) {
  193 + return array(null, new Error($url, $ret));
  194 + }
  195 + return array($ret->json(), null);
  196 + }
  197 +
  198 + private function post($url, $body, $contentType = 'application/json')
  199 + {
  200 + $rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
  201 + $rtcToken['Content-Type'] = $contentType;
  202 + $ret = Client::post($url, $body, $rtcToken);
  203 + if (!$ret->ok()) {
  204 + return array(null, new Error($url, $ret));
  205 + }
  206 + $r = ($ret->body === null) ? array() : $ret->json();
  207 + return array($r, null);
  208 + }
  209 +}
  1 +<?php
  2 +namespace Qiniu\Sms;
  3 +
  4 +use Qiniu\Http\Client;
  5 +use Qiniu\Http\Error;
  6 +use Qiniu\Config;
  7 +use Qiniu\Auth;
  8 +
  9 +class Sms
  10 +{
  11 + private $auth;
  12 + private $baseURL;
  13 +
  14 + public function __construct(Auth $auth)
  15 + {
  16 + $this->auth = $auth;
  17 +
  18 + $this->baseURL = sprintf("%s/%s/", Config::SMS_HOST, Config::SMS_VERSION);
  19 + }
  20 +
  21 + /*
  22 + * 创建签名
  23 + * signature: string 类型,必填,【长度限制8个字符内】超过长度会报错
  24 + * source: string 类型,必填,申请签名时必须指定签名来源。取值范围为:
  25 + nterprises_and_institutions 企事业单位的全称或简称
  26 + website 工信部备案网站的全称或简称
  27 + app APP应用的全称或简称
  28 + public_number_or_small_program 公众号或小程序的全称或简称
  29 + store_name 电商平台店铺名的全称或简称
  30 + trade_name 商标名的全称或简称,
  31 + * pics: 本地的图片路径 string 类型,可选
  32 + *@return: 类型array {
  33 + "signature_id": <signature_id>
  34 + }
  35 + */
  36 + public function createSignature($signature, $source, $pics = null)
  37 + {
  38 + $params['signature'] = $signature;
  39 + $params['source'] = $source;
  40 + if (!empty($pics)) {
  41 + $params['pics'] = $this->imgToBase64($pics);
  42 + }
  43 + $body = json_encode($params);
  44 + $url =$this->baseURL.'signature';
  45 + $ret = $this->post($url, $body);
  46 + return $ret;
  47 + }
  48 +
  49 + /*
  50 + * 编辑签名
  51 + * id 签名id : string 类型,必填,
  52 + * signature: string 类型,必填,
  53 + * source: string 类型,必填,申请签名时必须指定签名来源。取值范围为:
  54 + enterprises_and_institutions 企事业单位的全称或简称
  55 + website 工信部备案网站的全称或简称
  56 + app APP应用的全称或简称
  57 + public_number_or_small_program 公众号或小程序的全称或简称
  58 + store_name 电商平台店铺名的全称或简称
  59 + trade_name 商标名的全称或简称,
  60 + * pics: 本地的图片路径 string 类型,可选,
  61 + * @return: 类型array {
  62 + "signature": string
  63 + }
  64 + */
  65 + public function updateSignature($id, $signature, $source, $pics = null)
  66 + {
  67 + $params['signature'] = $signature;
  68 + $params['source'] = $source;
  69 + if (!empty($pics)) {
  70 + $params['pics'] = $this->imgToBase64($pics);
  71 + }
  72 + $body = json_encode($params);
  73 + $url =$this->baseURL.'signature/'.$id;
  74 + $ret = $this->PUT($url, $body);
  75 + return $ret;
  76 + }
  77 +
  78 + /*
  79 + * 查询签名
  80 + * audit_status: 审核状态 string 类型,可选,
  81 + 取值范围为: "passed"(通过), "rejected"(未通过), "reviewing"(审核中)
  82 + * page:页码 int 类型,
  83 + * page_size: 分页大小 int 类型,可选, 默认为20
  84 + *@return: 类型array {
  85 + "items": [{
  86 + "id": string,
  87 + "signature": string,
  88 + "source": string,
  89 + "audit_status": string,
  90 + "reject_reason": string,
  91 + "created_at": int64,
  92 + "updated_at": int64
  93 + }...],
  94 + "total": int,
  95 + "page": int,
  96 + "page_size": int,
  97 + }
  98 + */
  99 + public function checkSignature($audit_status = null, $page = 1, $page_size = 20)
  100 + {
  101 +
  102 + $url = sprintf(
  103 + "%s?audit_status=%s&page=%s&page_size=%s",
  104 + $this->baseURL.'signature',
  105 + $audit_status,
  106 + $page,
  107 + $page_size
  108 + );
  109 + $ret = $this->get($url);
  110 + return $ret;
  111 + }
  112 +
  113 +
  114 + /*
  115 + * 删除签名
  116 + * id 签名id string 类型,必填,
  117 + * @retrun : 请求成功 HTTP 状态码为 200
  118 + */
  119 + public function deleteSignature($id)
  120 + {
  121 + $url = $this->baseURL . 'signature/' . $id;
  122 + list(, $err) = $this->delete($url);
  123 + return $err;
  124 + }
  125 +
  126 +
  127 +
  128 +
  129 + /*
  130 + * 创建模板
  131 + * name : 模板名称 string 类型 ,必填
  132 + * template: 模板内容 string 类型,必填
  133 + * type: 模板类型 string 类型,必填,
  134 + 取值范围为: notification (通知类短信), verification (验证码短信), marketing (营销类短信)
  135 + * description: 申请理由简述 string 类型,必填
  136 + * signature_id: 已经审核通过的签名 string 类型,必填
  137 + * @return: 类型 array {
  138 + "template_id": string
  139 + }
  140 + */
  141 + public function createTemplate(
  142 + $name,
  143 + $template,
  144 + $type,
  145 + $description,
  146 + $signture_id
  147 + ) {
  148 + $params['name'] = $name;
  149 + $params['template'] = $template;
  150 + $params['type'] = $type;
  151 + $params['description'] = $description;
  152 + $params['signature_id'] = $signture_id;
  153 +
  154 + $body = json_encode($params);
  155 + $url =$this->baseURL.'template';
  156 + $ret = $this->post($url, $body);
  157 + return $ret;
  158 + }
  159 +
  160 + /*
  161 + * 查询模板
  162 + * audit_status: 审核状态 string 类型 ,可选,
  163 + 取值范围为: passed (通过), rejected (未通过), reviewing (审核中)
  164 + * page: 页码 int 类型,可选,默认为 1
  165 + * page_size: 分页大小 int 类型,可选,默认为 20
  166 + * @return: 类型array{
  167 + "items": [{
  168 + "id": string,
  169 + "name": string,
  170 + "template": string,
  171 + "audit_status": string,
  172 + "reject_reason": string,
  173 + "type": string,
  174 + "signature_id": string, // 模版绑定的签名ID
  175 + "signature_text": string, // 模版绑定的签名内容
  176 + "created_at": int64,
  177 + "updated_at": int64
  178 + }...],
  179 + "total": int,
  180 + "page": int,
  181 + "page_size": int
  182 + }
  183 + */
  184 + public function queryTemplate($audit_status = null, $page = 1, $page_size = 20)
  185 + {
  186 +
  187 + $url = sprintf(
  188 + "%s?audit_status=%s&page=%s&page_size=%s",
  189 + $this->baseURL.'template',
  190 + $audit_status,
  191 + $page,
  192 + $page_size
  193 + );
  194 + $ret = $this->get($url);
  195 + return $ret;
  196 + }
  197 +
  198 + /*
  199 + * 编辑模板
  200 + * id :模板id
  201 + * name : 模板名称 string 类型 ,必填
  202 + * template: 模板内容 string 类型,必填
  203 + * description: 申请理由简述 string 类型,必填
  204 + * signature_id: 已经审核通过的签名 string 类型,必填
  205 + * @retrun : 请求成功 HTTP 状态码为 200
  206 + */
  207 + public function updateTemplate(
  208 + $id,
  209 + $name,
  210 + $template,
  211 + $description,
  212 + $signature_id
  213 + ) {
  214 + $params['name'] = $name;
  215 + $params['template'] = $template;
  216 + $params['description'] = $description;
  217 + $params['signature_id'] = $signature_id;
  218 + $body = json_encode($params);
  219 + $url =$this->baseURL.'template/'.$id;
  220 + $ret = $this->PUT($url, $body);
  221 + return $ret;
  222 + }
  223 +
  224 + /*
  225 + * 删除模板
  226 + * id :模板id string 类型,必填,
  227 + * @retrun : 请求成功 HTTP 状态码为 200
  228 + */
  229 + public function deleteTemplate($id)
  230 + {
  231 + $url = $this->baseURL . 'template/' . $id;
  232 + list(, $err) = $this->delete($url);
  233 + return $err;
  234 + }
  235 +
  236 + /*
  237 + * 发送短信
  238 + * 编辑模板
  239 + * template_id :模板id string类型,必填
  240 + * mobiles : 手机号数组 []string 类型 ,必填
  241 + * parameters: 模板内容 map[string]string 类型,可选
  242 + * @return: 类型json {
  243 + "job_id": string
  244 + }
  245 + */
  246 + public function sendMessage($template_id, $mobiles, $parameters = null)
  247 + {
  248 + $params['template_id'] = $template_id;
  249 + $params['mobiles'] = $mobiles;
  250 + if (!empty($parameters)) {
  251 + $params['parameters'] = $parameters;
  252 + }
  253 + $body = json_encode($params);
  254 + $url =$this->baseURL.'message';
  255 + $ret = $this->post($url, $body);
  256 + return $ret;
  257 + }
  258 +
  259 + public function imgToBase64($img_file)
  260 + {
  261 + $img_base64 = '';
  262 + if (file_exists($img_file)) {
  263 + $app_img_file = $img_file; // 图片路径
  264 + $img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
  265 + $fp = fopen($app_img_file, "r"); // 图片是否可读权限
  266 + if ($fp) {
  267 + $filesize = filesize($app_img_file);
  268 + if ($filesize > 5*1024*1024) {
  269 + die("pic size < 5M !");
  270 + }
  271 + $content = fread($fp, $filesize);
  272 + $file_content = chunk_split(base64_encode($content)); // base64编码
  273 + switch ($img_info[2]) { //判读图片类型
  274 + case 1:
  275 + $img_type = 'gif';
  276 + break;
  277 + case 2:
  278 + $img_type = 'jpg';
  279 + break;
  280 + case 3:
  281 + $img_type = 'png';
  282 + break;
  283 + }
  284 + //合成图片的base64编码
  285 + $img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;
  286 + }
  287 + fclose($fp);
  288 + }
  289 +
  290 + return $img_base64;
  291 + }
  292 +
  293 + private function get($url, $cType = null)
  294 + {
  295 + $rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
  296 + $rtcToken['Content-Type'] = $cType;
  297 + $ret = Client::get($url, $rtcToken);
  298 + if (!$ret->ok()) {
  299 + return array(null, new Error($url, $ret));
  300 + }
  301 + return array($ret->json(), null);
  302 + }
  303 +
  304 + private function delete($url, $contentType = 'application/json')
  305 + {
  306 + $rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
  307 + $rtcToken['Content-Type'] = $contentType;
  308 + $ret = Client::delete($url, $rtcToken);
  309 + if (!$ret->ok()) {
  310 + return array(null, new Error($url, $ret));
  311 + }
  312 + return array($ret->json(), null);
  313 + }
  314 +
  315 + private function post($url, $body, $contentType = 'application/json')
  316 + {
  317 + $rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
  318 + $rtcToken['Content-Type'] = $contentType;
  319 + $ret = Client::post($url, $body, $rtcToken);
  320 + if (!$ret->ok()) {
  321 + return array(null, new Error($url, $ret));
  322 + }
  323 + $r = ($ret->body === null) ? array() : $ret->json();
  324 + return array($r, null);
  325 + }
  326 + private function PUT($url, $body, $contentType = 'application/json')
  327 + {
  328 + $rtcToken = $this->auth->authorizationV2($url, "PUT", $body, $contentType);
  329 + $rtcToken['Content-Type'] = $contentType;
  330 + $ret = Client::put($url, $body, $rtcToken);
  331 + if (!$ret->ok()) {
  332 + return array(null, new Error($url, $ret));
  333 + }
  334 + $r = ($ret->body === null) ? array() : $ret->json();
  335 + return array($r, null);
  336 + }
  337 +}
  1 +<?php
  2 +namespace Qiniu\Storage;
  3 +
  4 +use Qiniu\Auth;
  5 +use Qiniu\Config;
  6 +use Qiniu\Zone;
  7 +use Qiniu\Http\Client;
  8 +use Qiniu\Http\Error;
  9 +
  10 +/**
  11 + * 主要涉及了鉴黄接口的实现,具体的接口规格可以参考
  12 + *
  13 + * @link https://developer.qiniu.com/dora/manual/3674/kodo-product-introduction
  14 + */
  15 +final class ArgusManager
  16 +{
  17 + private $auth;
  18 + private $config;
  19 +
  20 + public function __construct(Auth $auth, Config $config = null)
  21 + {
  22 + $this->auth = $auth;
  23 + if ($config == null) {
  24 + $this->config = new Config();
  25 + } else {
  26 + $this->config = $config;
  27 + }
  28 + }
  29 +
  30 + /**
  31 + * 视频鉴黄
  32 + *
  33 + * @param $body body信息
  34 + * @param $vid videoID
  35 + *
  36 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  37 + * @link https://developer.qiniu.com/dora/manual/4258/video-pulp
  38 + */
  39 + public function pulpVideo($body, $vid)
  40 + {
  41 + $path = '/v1/video/' . $vid;
  42 +
  43 + return $this->arPost($path, $body);
  44 + }
  45 +
  46 + private function getArHost()
  47 + {
  48 + $scheme = "http://";
  49 + if ($this->config->useHTTPS == true) {
  50 + $scheme = "https://";
  51 + }
  52 + return $scheme . Config::ARGUS_HOST;
  53 + }
  54 +
  55 + private function arPost($path, $body = null)
  56 + {
  57 + $url = $this->getArHost() . $path;
  58 + return $this->post($url, $body);
  59 + }
  60 +
  61 + private function post($url, $body)
  62 + {
  63 + $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
  64 + $headers['Content-Type']='application/json';
  65 + $ret = Client::post($url, $body, $headers);
  66 + if (!$ret->ok()) {
  67 + print($ret->statusCode);
  68 + return array(null, new Error($url, $ret));
  69 + }
  70 + $r = ($ret->body === null) ? array() : $ret->json();
  71 + return array($r, null);
  72 + }
  73 +}
  1 +<?php
  2 +namespace Qiniu\Storage;
  3 +
  4 +use Qiniu\Auth;
  5 +use Qiniu\Config;
  6 +use Qiniu\Zone;
  7 +use Qiniu\Http\Client;
  8 +use Qiniu\Http\Error;
  9 +
  10 +/**
  11 + * 主要涉及了空间资源管理及批量操作接口的实现,具体的接口规格可以参考
  12 + *
  13 + * @link https://developer.qiniu.com/kodo/api/1274/rs
  14 + */
  15 +final class BucketManager
  16 +{
  17 + private $auth;
  18 + private $config;
  19 +
  20 + public function __construct(Auth $auth, Config $config = null)
  21 + {
  22 + $this->auth = $auth;
  23 + if ($config == null) {
  24 + $this->config = new Config();
  25 + } else {
  26 + $this->config = $config;
  27 + }
  28 + }
  29 +
  30 + /**
  31 + * 获取指定账号下所有的空间名。
  32 + *
  33 + * @return string[] 包含所有空间名
  34 + */
  35 + public function buckets($shared = true)
  36 + {
  37 + $includeShared = "false";
  38 + if ($shared === true) {
  39 + $includeShared = "true";
  40 + }
  41 + return $this->rsGet('/buckets?shared=' . $includeShared);
  42 + }
  43 +
  44 + /**
  45 + * 列举空间,返回bucket列表
  46 + * region 指定区域,global 指定全局空间。
  47 + * 在指定了 region 参数时,
  48 + * 如果指定 global 为 true,那么忽略 region 参数指定的区域,返回所有区域的全局空间。
  49 + * 如果没有指定 global 为 true,那么返回指定区域中非全局空间。
  50 + * 在没有指定 region 参数时(包括指定为空""),
  51 + * 如果指定 global 为 true,那么返回所有区域的全局空间。
  52 + * 如果没有指定 global 为 true,那么返回指定区域中所有的空间,包括全局空间。
  53 + * 在指定了line为 true 时,只返回 Line 空间;否则,只返回非 Line 空间。
  54 + * share 参数用于指定共享空间。
  55 + */
  56 +
  57 + public function listbuckets(
  58 + $region = null,
  59 + $line = 'false',
  60 + $shared = 'false'
  61 + ) {
  62 + $path = '/v3/buckets?region=' . $region . '&line=' . $line . '&shared=' . $shared;
  63 + $info = $this->ucPost($path);
  64 + return $info;
  65 + }
  66 +
  67 + /**
  68 + * 创建空间
  69 + *
  70 + * @param $name 创建的空间名
  71 + * @param $region 创建的区域,默认华东
  72 + *
  73 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  74 + */
  75 + public function createBucket($name, $region = 'z0')
  76 + {
  77 + $path = '/mkbucketv2/'.$name.'/region/' . $region;
  78 + return $this->rsPost($path, null);
  79 + }
  80 +
  81 + /**
  82 + * 删除空间
  83 + *
  84 + * @param $name 删除的空间名
  85 + *
  86 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  87 + */
  88 + public function deleteBucket($name)
  89 + {
  90 + $path = '/drop/'.$name;
  91 + return $this->rsPost($path, null);
  92 + }
  93 +
  94 + /**
  95 + * 获取指定空间绑定的所有的域名
  96 + *
  97 + * @return string[] 包含所有空间域名
  98 + */
  99 + public function domains($bucket)
  100 + {
  101 + return $this->apiGet('/v6/domain/list?tbl=' . $bucket);
  102 + }
  103 +
  104 + /**
  105 + * 获取指定空间的相关信息
  106 + *
  107 + * @return string[] 包含空间信息
  108 + */
  109 + public function bucketInfo($bucket)
  110 + {
  111 + $path = '/v2/bucketInfo?bucket=' . $bucket;
  112 + $info = $this->ucPost($path);
  113 + return $info;
  114 + }
  115 +
  116 + /**
  117 + * 获取指定zone的空间信息列表
  118 + * 在Region 未指定且Global 不为 true 时(包含未指定的情况,下同),返回用户的所有空间。
  119 + * 在指定了 region 参数且 global 不为 true 时,只列举非全局空间。
  120 + * shared 不指定shared参数或指定shared为rw或false时,返回包含具有读写权限空间,
  121 + * 指定shared为rd或true时,返回包含具有读权限空间。
  122 + * fs:如果为 true,会返回每个空间当前的文件数和存储量(实时数据)。
  123 + * @return string[] 包含空间信息
  124 + */
  125 + public function bucketInfos($region = null, $shared = 'false', $fs = 'false')
  126 + {
  127 + $path = '/v2/bucketInfos?region=' . $region . '&shared=' . $shared . '&fs=' . $fs;
  128 + $info = $this->ucPost($path);
  129 + return $info;
  130 + }
  131 +
  132 + /**
  133 + * 获取空间绑定的域名列表
  134 + * @return string[] 包含空间绑定的所有域名
  135 + */
  136 +
  137 + /**
  138 + * 列取空间的文件列表
  139 + *
  140 + * @param $bucket 空间名
  141 + * @param $prefix 列举前缀
  142 + * @param $marker 列举标识符
  143 + * @param $limit 单次列举个数限制
  144 + * @param $delimiter 指定目录分隔符
  145 + *
  146 + * @return array 包含文件信息的数组,类似:[
  147 +* {
  148 +* "hash" => "<Hash string>",
  149 +* "key" => "<Key string>",
  150 +* "fsize" => "<file size>",
  151 +* "putTime" => "<file modify time>"
  152 +* },
  153 +* ...
  154 +* ]
  155 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
  156 + */
  157 + public function listFiles(
  158 + $bucket,
  159 + $prefix = null,
  160 + $marker = null,
  161 + $limit = 1000,
  162 + $delimiter = null
  163 + ) {
  164 + $query = array('bucket' => $bucket);
  165 + \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
  166 + \Qiniu\setWithoutEmpty($query, 'marker', $marker);
  167 + \Qiniu\setWithoutEmpty($query, 'limit', $limit);
  168 + \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
  169 + $url = $this->getRsfHost() . '/list?' . http_build_query($query);
  170 + return $this->get($url);
  171 + }
  172 +
  173 + /**
  174 + * 列取空间的文件列表
  175 + *
  176 + * @param $bucket 空间名
  177 + * @param $prefix 列举前缀
  178 + * @param $marker 列举标识符
  179 + * @param $limit 单次列举个数限制
  180 + * @param $delimiter 指定目录分隔符
  181 + * @param $skipconfirm 是否跳过已删除条目的确认机制
  182 + *
  183 + * @return array 包含文件信息的数组,类似:[
  184 +* {
  185 +* "hash" => "<Hash string>",
  186 +* "key" => "<Key string>",
  187 +* "fsize" => "<file size>",
  188 +* "putTime" => "<file modify time>"
  189 +* },
  190 +* ...
  191 +* ]
  192 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
  193 + */
  194 + public function listFilesv2(
  195 + $bucket,
  196 + $prefix = null,
  197 + $marker = null,
  198 + $limit = 1000,
  199 + $delimiter = null,
  200 + $skipconfirm = true
  201 + ) {
  202 + $query = array('bucket' => $bucket);
  203 + \Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
  204 + \Qiniu\setWithoutEmpty($query, 'marker', $marker);
  205 + \Qiniu\setWithoutEmpty($query, 'limit', $limit);
  206 + \Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
  207 + \Qiniu\setWithoutEmpty($query, 'skipconfirm', $skipconfirm);
  208 + $path = '/v2/list?' . http_build_query($query);
  209 + $url = $this->getRsfHost() . $path;
  210 + $headers = $this->auth->authorization($url, null, 'application/x-www-form-urlencoded');
  211 + $ret = Client::post($url, null, $headers);
  212 + if (!$ret->ok()) {
  213 + return array(null, new Error($url, $ret));
  214 + }
  215 + $r = explode("\n", $ret->body);
  216 + $pop = array_pop($r);
  217 + return array($r, null);
  218 + }
  219 +
  220 + /**
  221 + * 设置Referer防盗链
  222 + *
  223 + * @param $bucket 空间名
  224 + * @param $mode 0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
  225 + * 1: 表示设置Referer白名单; 2:表示设置Referer黑名单
  226 + * @param $norefer 0: 表示不允许空 Refer 访问; 1: 表示允许空 Refer 访问
  227 + * @param $pattern 规则字符串, 当前允许格式分为三种: 一种为空主机头域名,
  228 + * 比如 foo.com; 一种是泛域名,比如 *.bar.com; 一种是完全通配符,
  229 + * 即一个 *; 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
  230 + * @param $source_enabled 源站是否支持,默认为0只给CDN配置, 设置为1表示开启源站防盗链
  231 + *
  232 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  233 + */
  234 + // public function referAntiLeech(){
  235 +
  236 + // }
  237 +
  238 + /**
  239 + * 增加bucket生命规则
  240 + *
  241 + * @param $bucket 空间名
  242 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,只能为
  243 + * 字母、数字、下划线
  244 + * @param $prefix 同一个 bucket 里面前缀不能重复
  245 + * @param $delete_after_days 指定上传文件多少天后删除,指定为0表示不删除,
  246 + * 大于0表示多少天后删除,需大于 to_line_after_days
  247 + * @param $to_line_after_days 指定文件上传多少天后转低频存储。指定为0表示
  248 + * 不转低频存储,小于0表示上传的文件立即变低频存储
  249 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  250 + */
  251 + public function bucketLifecycleRule(
  252 + $bucket,
  253 + $name,
  254 + $prefix,
  255 + $delete_after_days,
  256 + $to_line_after_days
  257 + ) {
  258 + $path = '/rules/add';
  259 + if ($bucket) {
  260 + $params['bucket'] = $bucket;
  261 + }
  262 + if ($name) {
  263 + $params['name'] = $name;
  264 + }
  265 + if ($prefix) {
  266 + $params['prefix'] = $prefix;
  267 + }
  268 + if ($delete_after_days) {
  269 + $params['delete_after_days'] = $delete_after_days;
  270 + }
  271 + if ($to_line_after_days) {
  272 + $params['to_line_after_days'] = $to_line_after_days;
  273 + }
  274 + $data = http_build_query($params);
  275 + $info = $this->ucPost($path, $data);
  276 + return $info;
  277 + }
  278 +
  279 + /**
  280 + * 更新bucket生命规则
  281 + *
  282 + * @param $bucket 空间名
  283 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、
  284 + * 数字、下划线
  285 + * @param $prefix 同一个 bucket 里面前缀不能重复
  286 + * @param $delete_after_days 指定上传文件多少天后删除,指定为0表示不删除,
  287 + * 大于0表示多少天后删除,需大于 to_line_after_days
  288 + * @param $to_line_after_days 指定文件上传多少天后转低频存储。指定为0表示不
  289 + * 转低频存储,小于0表示上传的文件立即变低频存储
  290 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  291 + */
  292 + public function updateBucketLifecycleRule(
  293 + $bucket,
  294 + $name,
  295 + $prefix,
  296 + $delete_after_days,
  297 + $to_line_after_days
  298 + ) {
  299 + $path = '/rules/update';
  300 + if ($bucket) {
  301 + $params['bucket'] = $bucket;
  302 + }
  303 + if ($name) {
  304 + $params['name'] = $name;
  305 + }
  306 + if ($prefix) {
  307 + $params['prefix'] = $prefix;
  308 + }
  309 + if ($delete_after_days) {
  310 + $params['delete_after_days'] = $delete_after_days;
  311 + }
  312 + if ($to_line_after_days) {
  313 + $params['to_line_after_days'] = $to_line_after_days;
  314 + }
  315 + $data = http_build_query($params);
  316 + $info = $this->ucPost($path, $data);
  317 + return $info;
  318 + }
  319 +
  320 + /**
  321 + * 获取bucket生命规则
  322 + *
  323 + * @param $bucket 空间名
  324 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  325 + */
  326 + public function getBucketLifecycleRules($bucket)
  327 + {
  328 + $path = '/rules/get?bucket=' . $bucket;
  329 + $info = $this->ucGet($path);
  330 + return $info;
  331 + }
  332 +
  333 + /**
  334 + * 删除bucket生命规则
  335 + *
  336 + * @param $bucket 空间名
  337 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
  338 + * 只能为字母、数字、下划线()
  339 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  340 + */
  341 + public function deleteBucketLifecycleRule($bucket, $name)
  342 + {
  343 + $path = '/rules/delete';
  344 + if ($bucket) {
  345 + $params['bucket'] = $bucket;
  346 + }
  347 + if ($name) {
  348 + $params['name'] = $name;
  349 + }
  350 + $data = http_build_query($params);
  351 + $info = $this->ucPost($path, $data);
  352 + return $info;
  353 + }
  354 +
  355 + /**
  356 + * 增加bucket事件通知规则
  357 + *
  358 + * @param $bucket 空间名
  359 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
  360 + * 只能为字母、数字、下划线()
  361 + * @param $prefix 同一个 bucket 里面前缀不能重复
  362 + * @param $suffix 可选,文件配置的后缀
  363 + * @param $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,
  364 + * disable,enable,deleteMarkerCreate
  365 + * @param $callbackURL 通知URL,可以指定多个,失败依次重试
  366 + * @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
  367 + * @param $host 可选,通知请求的host
  368 + *
  369 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  370 + */
  371 + public function putBucketEvent(
  372 + $bucket,
  373 + $name,
  374 + $prefix,
  375 + $suffix,
  376 + $event,
  377 + $callbackURL,
  378 + $access_key = null,
  379 + $host = null
  380 + ) {
  381 + $path = '/events/add';
  382 + if ($bucket) {
  383 + $params['bucket'] = $bucket;
  384 + }
  385 + if ($name) {
  386 + $params['name'] = $name;
  387 + }
  388 + if ($prefix) {
  389 + $params['prefix'] = $prefix;
  390 + }
  391 + if ($suffix) {
  392 + $params['suffix'] = $suffix;
  393 + }
  394 + if ($callbackURL) {
  395 + $params['callbackURL'] = $callbackURL;
  396 + }
  397 + if ($access_key) {
  398 + $params['access_key'] = $access_key;
  399 + }
  400 + if ($host) {
  401 + $params['host'] = $host;
  402 + }
  403 + $data = http_build_query($params);
  404 + if ($event) {
  405 + $eventpath = "";
  406 + foreach ($event as $key => $value) {
  407 + $eventpath .= "&event=$value";
  408 + }
  409 + $data .= $eventpath;
  410 + }
  411 + $info = $this->ucPost($path, $data);
  412 + return $info;
  413 + }
  414 +
  415 + /**
  416 + * 更新bucket事件通知规则
  417 + *
  418 + * @param $bucket 空间名
  419 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
  420 + * 只能为字母、数字、下划线()
  421 + * @param $prefix 同一个 bucket 里面前缀不能重复
  422 + * @param $suffix 可选,文件配置的后缀
  423 + * @param $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,disable,
  424 + * enable,deleteMarkerCreate
  425 + * @param $callbackURL 通知URL,可以指定多个,失败依次重试
  426 + * @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
  427 + * @param $host 可选,通知请求的host
  428 + *
  429 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  430 + */
  431 + public function updateBucketEvent(
  432 + $bucket,
  433 + $name,
  434 + $prefix,
  435 + $suffix,
  436 + $event,
  437 + $callbackURL,
  438 + $access_key = null,
  439 + $host = null
  440 + ) {
  441 + $path = '/events/update';
  442 + if ($bucket) {
  443 + $params['bucket'] = $bucket;
  444 + }
  445 + if ($name) {
  446 + $params['name'] = $name;
  447 + }
  448 + if ($prefix) {
  449 + $params['prefix'] = $prefix;
  450 + }
  451 + if ($suffix) {
  452 + $params['suffix'] = $suffix;
  453 + }
  454 + if ($event) {
  455 + $params['event'] = $event;
  456 + }
  457 + if ($callbackURL) {
  458 + $params['callbackURL'] = $callbackURL;
  459 + }
  460 + if ($access_key) {
  461 + $params['access_key'] = $access_key;
  462 + }
  463 + if ($host) {
  464 + $params['host'] = $host;
  465 + }
  466 + $data = http_build_query($params);
  467 + $info = $this->ucPost($path, $data);
  468 + return $info;
  469 + }
  470 +
  471 + /**
  472 + * 获取bucket事件通知规则
  473 + *
  474 + * @param $bucket 空间名
  475 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  476 + */
  477 + public function getBucketEvents($bucket)
  478 + {
  479 + $path = '/events/get?bucket=' . $bucket;
  480 + $info = $this->ucGet($path);
  481 + return $info;
  482 + }
  483 +
  484 + /**
  485 + * 删除bucket事件通知规则
  486 + *
  487 + * @param $bucket 空间名
  488 + * @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
  489 + * 只能为字母、数字、下划线
  490 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  491 + */
  492 + public function deleteBucketEvent($bucket, $name)
  493 + {
  494 + $path = '/events/delete';
  495 + if ($bucket) {
  496 + $params['bucket'] = $bucket;
  497 + }
  498 + if ($name) {
  499 + $params['name'] = $name;
  500 + }
  501 + $data = http_build_query($params);
  502 + $info = $this->ucPost($path, $data);
  503 + return $info;
  504 + }
  505 +
  506 + /**
  507 + * 设置bucket的跨域信息,最多允许设置10条跨域规则。
  508 + * 对于同一个域名如果设置了多条规则,那么按顺序使用第一条匹配的规则去生成返回值。
  509 + * 对于简单跨域请求,只匹配 Origin;
  510 + * allowed_orgin: 允许的域名。必填;支持通配符*;*表示全部匹配;只有第一个*生效;
  511 + * 需要设置"Scheme";大小写敏感。例如
  512 + * 规则:http://*.abc.*.com 请求:"http://test.abc.test.com" 结果:不通过
  513 + * 规则:"http://abc.com" 请求:"https://abc.com"/"abc.com" 结果:不通过
  514 + * 规则:"abc.com" 请求:"http://abc.com" 结果:不通过
  515 + * allowed_method: 允许的方法。必填;不支持通配符;大小写不敏感;
  516 + * allowed_header: 允许的header。选填;支持通配符*,
  517 + * 但只能是单独的*,表示允许全部header,其他*不生效;
  518 + * 空则不允许任何header;大小写不敏感;
  519 + * exposed_header: 暴露的header。选填;不支持通配符;
  520 + * X-Log, X-Reqid是默认会暴露的两个header;
  521 + * 其他的header如果没有设置,则不会暴露;大小写不敏感;
  522 + * max_age: 结果可以缓存的时间。选填;空则不缓存;
  523 + * allowed_credentials:该配置不支持设置,默认为true。
  524 + * 备注:如果没有设置任何corsRules,那么默认允许所有的跨域请求
  525 + */
  526 + // public function putCorsRules(string $bucket, array $params)
  527 + // {
  528 + // $path = '/corsRules/set/' . $bucket;
  529 + // $data = json_encode($params);
  530 + // $info = $this->ucPost($path, $data);
  531 + // return $info;
  532 + // }
  533 +
  534 + /**
  535 + * 获取bucket的跨域信息
  536 + * $bucket 空间名
  537 + */
  538 + public function getCorsRules($bucket)
  539 + {
  540 + $path = '/corsRules/get/' . $bucket;
  541 + $info = $this->ucGet($path);
  542 + return $info;
  543 + }
  544 +
  545 + /**
  546 + * 设置回源规则
  547 + * 使用该API设置源站优先级高于/image设置的源站,即IO优先读取source接口设置的源站配置,
  548 + * 如果存在会忽略/image设置的源站
  549 + * Bucket 空间名
  550 + * Host(可选)回源Host
  551 + * RetryCodes(可选),镜像回源时源站返回Code可以重试,最多指定3个,当前只支持4xx错误码重试
  552 + * SourceQiniuAK,SourceQiniuSK(可选)如果存在将在回源时对URL进行签名,客户源站可以验证
  553 + * 以保证请求来自Qiniu服务器
  554 + * Expires(可选) 签名过期时间,如果不设置默认为1小时
  555 + * Addr 回源地址,不可重复。
  556 + * Weight 权重,范围限制1-100,不填默认为1,回源时会根据所有源的权重值进行源站选择,
  557 + * 主备源会分开计算.
  558 + * Backup 是否备用回源,回源优先尝试主源
  559 + */
  560 + // public function putBucktSourceConfig(array $params)
  561 + // {
  562 + // $path = '/mirrorConfig/set';
  563 + // $data = json_encode($params);
  564 + // $info = $this->ucPostV2($path, $data);
  565 + // return $info;
  566 + // }
  567 +
  568 + /**
  569 + * 获取空间回源配置
  570 + */
  571 + public function getBucktSourceConfig(array $params)
  572 + {
  573 + $path = '/mirrorConfig/get';
  574 + $data = json_encode($params);
  575 + $info = $this->ucPostV2($path, $data);
  576 + return $info;
  577 + }
  578 +
  579 + /**
  580 + * 开关原图保护
  581 + * mode 为1表示开启原图保护,0表示关闭
  582 + */
  583 + public function putBucketAccessStyleMode($bucket, $mode)
  584 + {
  585 + $path = '/accessMode/' . $bucket . '/mode/' . $mode;
  586 + $info = $this->ucPost($path, null);
  587 + return $info;
  588 + }
  589 +
  590 + /**
  591 + * 设置私有属性
  592 + * private为0表示公开,为1表示私有
  593 + */
  594 + public function putBucketAccessMode($bucket, $private)
  595 + {
  596 + $path = '/bucket/' . $bucket . '/private/' . $private;
  597 + $info = $this->ucPost($path, null);
  598 + return $info;
  599 + }
  600 +
  601 + /**
  602 + * 设置referer防盗链
  603 + * bucket=<BucketName>: bucket 名
  604 + * mode=<AntiLeechMode>:
  605 + * 0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
  606 + * 1: 表示设置Referer白名单; 2: 表示设置Referer黑名单
  607 + * norefer=<NoRefer>: 0: 表示不允许空 Refer 访问;
  608 + * 1: 表示允许空 Refer 访问
  609 + * pattern=<Pattern>: 规则字符串, 当前允许格式分为三种:
  610 + * 一种为空主机头域名, 比如 foo.com;
  611 + * 一种是泛域名, 比如 *.bar.com; 一种是完全通配符, 即一个 *;
  612 + * 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
  613 + * 空主机头域名可以是多级域名,比如 foo.bar.com。
  614 + * 多个域名之间不允许夹带空白字符。
  615 + * source_enabled=:1
  616 + */
  617 + public function putReferAntiLeech($bucket, $mode, $norefer, $pattern, $enabled = 1)
  618 + {
  619 + $path = "/referAntiLeech?bucket=$bucket&mode=$mode&norefer=$norefer&pattern=$pattern&source_enabled=$enabled";
  620 + $info = $this->ucPost($path, null);
  621 + return $info;
  622 + }
  623 +
  624 + /**
  625 + * 设置Bucket的maxAge
  626 + * maxAge为0或者负数表示为默认值(31536000)
  627 + */
  628 + public function putBucketMaxAge($bucket, $maxAge)
  629 + {
  630 + $path = '/maxAge?bucket=' . $bucket . '&maxAge=' . $maxAge;
  631 + $info = $this->ucPost($path, null);
  632 + return $info;
  633 + }
  634 +
  635 + /**
  636 + * 设置配额
  637 + * <bucket>: 空间名称,不支持授权空间
  638 + * <size>: 空间存储量配额,参数传入0或不传表示不更改当前配置,传入-1表示取消限额,
  639 + * 新创建的空间默认没有限额。
  640 + * <count>: 空间文件数配额,参数含义同<size>
  641 + */
  642 + public function putBucketQuota($bucket, $size, $count)
  643 + {
  644 + $path = '/setbucketquota/' . $bucket . '/size/' . $size . '/count/' . $count;
  645 + $info = $this->apiPost($path, null);
  646 + return $info;
  647 + }
  648 +
  649 + /**
  650 + * 获取配额
  651 + * bucket 空间名称
  652 + */
  653 + public function getBucketQuota($bucket)
  654 + {
  655 + $path = '/getbucketquota/' . $bucket;
  656 + $info = $this->apiPost($path, null);
  657 + return $info;
  658 + }
  659 +
  660 + /**
  661 + * 获取资源的元信息,但不返回文件内容
  662 + *
  663 + * @param $bucket 待获取信息资源所在的空间
  664 + * @param $key 待获取资源的文件名
  665 + *
  666 + * @return array 包含文件信息的数组,类似:
  667 +* [
  668 +* "hash" => "<Hash string>",
  669 +* "key" => "<Key string>",
  670 +* "fsize" => <file size>,
  671 +* "putTime" => "<file modify time>"
  672 +* "fileType" => <file type>
  673 +* ]
  674 + *
  675 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/stat.html
  676 + */
  677 + public function stat($bucket, $key)
  678 + {
  679 + $path = '/stat/' . \Qiniu\entry($bucket, $key);
  680 + return $this->rsGet($path);
  681 + }
  682 +
  683 + /**
  684 + * 删除指定资源
  685 + *
  686 + * @param $bucket 待删除资源所在的空间
  687 + * @param $key 待删除资源的文件名
  688 + *
  689 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  690 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/delete.html
  691 + */
  692 + public function delete($bucket, $key)
  693 + {
  694 + $path = '/delete/' . \Qiniu\entry($bucket, $key);
  695 + list(, $error) = $this->rsPost($path);
  696 + return $error;
  697 + }
  698 +
  699 +
  700 + /**
  701 + * 给资源进行重命名,本质为move操作。
  702 + *
  703 + * @param $bucket 待操作资源所在空间
  704 + * @param $oldname 待操作资源文件名
  705 + * @param $newname 目标资源文件名
  706 + *
  707 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  708 + */
  709 + public function rename($bucket, $oldname, $newname)
  710 + {
  711 + return $this->move($bucket, $oldname, $bucket, $newname);
  712 + }
  713 +
  714 + /**
  715 + * 对资源进行复制。
  716 + *
  717 + * @param $from_bucket 待操作资源所在空间
  718 + * @param $from_key 待操作资源文件名
  719 + * @param $to_bucket 目标资源空间名
  720 + * @param $to_key 目标资源文件名
  721 + *
  722 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  723 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/copy.html
  724 + */
  725 + public function copy($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
  726 + {
  727 + $from = \Qiniu\entry($from_bucket, $from_key);
  728 + $to = \Qiniu\entry($to_bucket, $to_key);
  729 + $path = '/copy/' . $from . '/' . $to;
  730 + if ($force === true) {
  731 + $path .= '/force/true';
  732 + }
  733 + list(, $error) = $this->rsPost($path);
  734 + return $error;
  735 + }
  736 +
  737 + /**
  738 + * 将资源从一个空间到另一个空间
  739 + *
  740 + * @param $from_bucket 待操作资源所在空间
  741 + * @param $from_key 待操作资源文件名
  742 + * @param $to_bucket 目标资源空间名
  743 + * @param $to_key 目标资源文件名
  744 + *
  745 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  746 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/move.html
  747 + */
  748 + public function move($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
  749 + {
  750 + $from = \Qiniu\entry($from_bucket, $from_key);
  751 + $to = \Qiniu\entry($to_bucket, $to_key);
  752 + $path = '/move/' . $from . '/' . $to;
  753 + if ($force) {
  754 + $path .= '/force/true';
  755 + }
  756 + list(, $error) = $this->rsPost($path);
  757 + return $error;
  758 + }
  759 +
  760 + /**
  761 + * 主动修改指定资源的文件元信息
  762 + *
  763 + * @param $bucket 待操作资源所在空间
  764 + * @param $key 待操作资源文件名
  765 + * @param $mime 待操作文件目标mimeType
  766 + *
  767 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  768 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/chgm.html
  769 + */
  770 + public function changeMime($bucket, $key, $mime)
  771 + {
  772 + $resource = \Qiniu\entry($bucket, $key);
  773 + $encode_mime = \Qiniu\base64_urlSafeEncode($mime);
  774 + $path = '/chgm/' . $resource . '/mime/' . $encode_mime;
  775 + list(, $error) = $this->rsPost($path);
  776 + return $error;
  777 + }
  778 +
  779 +
  780 + /**
  781 + * 修改指定资源的存储类型
  782 + *
  783 + * @param $bucket 待操作资源所在空间
  784 + * @param $key 待操作资源文件名
  785 + * @param $fileType 待操作文件目标文件类型
  786 + *
  787 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  788 + * @link https://developer.qiniu.com/kodo/api/3710/modify-the-file-type
  789 + */
  790 + public function changeType($bucket, $key, $fileType)
  791 + {
  792 + $resource = \Qiniu\entry($bucket, $key);
  793 + $path = '/chtype/' . $resource . '/type/' . $fileType;
  794 + list(, $error) = $this->rsPost($path);
  795 + return $error;
  796 + }
  797 +
  798 + /**
  799 + * 修改文件的存储状态,即禁用状态和启用状态间的的互相转换
  800 + *
  801 + * @param $bucket 待操作资源所在空间
  802 + * @param $key 待操作资源文件名
  803 + * @param $status 待操作文件目标文件类型
  804 + *
  805 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  806 + * @link https://developer.qiniu.com/kodo/api/4173/modify-the-file-status
  807 + */
  808 + public function changeStatus($bucket, $key, $status)
  809 + {
  810 + $resource = \Qiniu\entry($bucket, $key);
  811 + $path = '/chstatus/' . $resource . '/status/' . $status;
  812 + list(, $error) = $this->rsPost($path);
  813 + return $error;
  814 + }
  815 +
  816 + /**
  817 + * 从指定URL抓取资源,并将该资源存储到指定空间中
  818 + *
  819 + * @param $url 指定的URL
  820 + * @param $bucket 目标资源空间
  821 + * @param $key 目标资源文件名
  822 + *
  823 + * @return array 包含已拉取的文件信息。
  824 + * 成功时: [
  825 + * [
  826 + * "hash" => "<Hash string>",
  827 + * "key" => "<Key string>"
  828 + * ],
  829 + * null
  830 + * ]
  831 + *
  832 + * 失败时: [
  833 + * null,
  834 + * Qiniu/Http/Error
  835 + * ]
  836 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/fetch.html
  837 + */
  838 + public function fetch($url, $bucket, $key = null)
  839 + {
  840 +
  841 + $resource = \Qiniu\base64_urlSafeEncode($url);
  842 + $to = \Qiniu\entry($bucket, $key);
  843 + $path = '/fetch/' . $resource . '/to/' . $to;
  844 +
  845 + $ak = $this->auth->getAccessKey();
  846 + $ioHost = $this->config->getIovipHost($ak, $bucket);
  847 +
  848 + $url = $ioHost . $path;
  849 + return $this->post($url, null);
  850 + }
  851 +
  852 + /**
  853 + * 从镜像源站抓取资源到空间中,如果空间中已经存在,则覆盖该资源
  854 + *
  855 + * @param $bucket 待获取资源所在的空间
  856 + * @param $key 代获取资源文件名
  857 + *
  858 + * @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
  859 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/prefetch.html
  860 + */
  861 + public function prefetch($bucket, $key)
  862 + {
  863 + $resource = \Qiniu\entry($bucket, $key);
  864 + $path = '/prefetch/' . $resource;
  865 +
  866 + $ak = $this->auth->getAccessKey();
  867 + $ioHost = $this->config->getIovipHost($ak, $bucket);
  868 +
  869 + $url = $ioHost . $path;
  870 + list(, $error) = $this->post($url, null);
  871 + return $error;
  872 + }
  873 +
  874 + /**
  875 + * 在单次请求中进行多个资源管理操作
  876 + *
  877 + * @param $operations 资源管理操作数组
  878 + *
  879 + * @return array 每个资源的处理情况,结果类似:
  880 + * [
  881 + * { "code" => <HttpCode int>, "data" => <Data> },
  882 + * { "code" => <HttpCode int> },
  883 + * { "code" => <HttpCode int> },
  884 + * { "code" => <HttpCode int> },
  885 + * { "code" => <HttpCode int>, "data" => { "error": "<ErrorMessage string>" } },
  886 + * ...
  887 + * ]
  888 + * @link http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html
  889 + */
  890 + public function batch($operations)
  891 + {
  892 + $params = 'op=' . implode('&op=', $operations);
  893 + return $this->rsPost('/batch', $params);
  894 + }
  895 +
  896 + /**
  897 + * 设置文件的生命周期
  898 + *
  899 + * @param $bucket 设置文件生命周期文件所在的空间
  900 + * @param $key 设置文件生命周期文件的文件名
  901 + * @param $days 设置该文件多少天后删除,当$days设置为0时表示取消该文件的生命周期
  902 + *
  903 + * @return Mixed
  904 + * @link https://developer.qiniu.com/kodo/api/update-file-lifecycle
  905 + */
  906 + public function deleteAfterDays($bucket, $key, $days)
  907 + {
  908 + $entry = \Qiniu\entry($bucket, $key);
  909 + $path = "/deleteAfterDays/$entry/$days";
  910 + list(, $error) = $this->rsPost($path);
  911 + return $error;
  912 + }
  913 +
  914 + private function getRsfHost()
  915 + {
  916 + $scheme = "http://";
  917 + if ($this->config->useHTTPS == true) {
  918 + $scheme = "https://";
  919 + }
  920 + return $scheme . Config::RSF_HOST;
  921 + }
  922 +
  923 + private function getRsHost()
  924 + {
  925 + $scheme = "http://";
  926 + if ($this->config->useHTTPS == true) {
  927 + $scheme = "https://";
  928 + }
  929 + return $scheme . Config::RS_HOST;
  930 + }
  931 +
  932 + private function getApiHost()
  933 + {
  934 + $scheme = "http://";
  935 + if ($this->config->useHTTPS == true) {
  936 + $scheme = "https://";
  937 + }
  938 + return $scheme . Config::API_HOST;
  939 + }
  940 +
  941 + private function getUcHost()
  942 + {
  943 + $scheme = "http://";
  944 + if ($this->config->useHTTPS == true) {
  945 + $scheme = "https://";
  946 + }
  947 + return $scheme . Config::UC_HOST;
  948 + }
  949 +
  950 + private function rsPost($path, $body = null)
  951 + {
  952 + $url = $this->getRsHost() . $path;
  953 + return $this->post($url, $body);
  954 + }
  955 +
  956 + private function apiPost($path, $body = null)
  957 + {
  958 + $url = $this->getApiHost() . $path;
  959 + return $this->post($url, $body);
  960 + }
  961 +
  962 + private function ucPost($path, $body = null)
  963 + {
  964 + $url = $this->getUcHost() . $path;
  965 + return $this->post($url, $body);
  966 + }
  967 +
  968 + private function ucGet($path)
  969 + {
  970 + $url = $this->getUcHost() . $path;
  971 + return $this->get($url);
  972 + }
  973 +
  974 + private function apiGet($path)
  975 + {
  976 + $url = $this->getApiHost() . $path;
  977 + return $this->get($url);
  978 + }
  979 +
  980 + private function rsGet($path)
  981 + {
  982 + $url = $this->getRsHost() . $path;
  983 + return $this->get($url);
  984 + }
  985 +
  986 + private function get($url)
  987 + {
  988 + $headers = $this->auth->authorization($url);
  989 + $ret = Client::get($url, $headers);
  990 + if (!$ret->ok()) {
  991 + return array(null, new Error($url, $ret));
  992 + }
  993 + return array($ret->json(), null);
  994 + }
  995 +
  996 + private function post($url, $body)
  997 + {
  998 + $headers = $this->auth->authorization($url, $body, 'application/x-www-form-urlencoded');
  999 + $ret = Client::post($url, $body, $headers);
  1000 + if (!$ret->ok()) {
  1001 + return array(null, new Error($url, $ret));
  1002 + }
  1003 + $r = ($ret->body === null) ? array() : $ret->json();
  1004 + return array($r, null);
  1005 + }
  1006 +
  1007 + private function ucPostV2($path, $body)
  1008 + {
  1009 + $url = $this->getUcHost() . $path;
  1010 + return $this->postV2($url, $body);
  1011 + }
  1012 +
  1013 + private function postV2($url, $body)
  1014 + {
  1015 + $headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
  1016 + $headers["Content-Type"] = 'application/json';
  1017 + $ret = Client::post($url, $body, $headers);
  1018 + if (!$ret->ok()) {
  1019 + return array(null, new Error($url, $ret));
  1020 + }
  1021 + $r = ($ret->body === null) ? array() : $ret->json();
  1022 + return array($r, null);
  1023 + }
  1024 +
  1025 + public static function buildBatchCopy($source_bucket, $key_pairs, $target_bucket, $force)
  1026 + {
  1027 + return self::twoKeyBatch('/copy', $source_bucket, $key_pairs, $target_bucket, $force);
  1028 + }
  1029 +
  1030 +
  1031 + public static function buildBatchRename($bucket, $key_pairs, $force)
  1032 + {
  1033 + return self::buildBatchMove($bucket, $key_pairs, $bucket, $force);
  1034 + }
  1035 +
  1036 +
  1037 + public static function buildBatchMove($source_bucket, $key_pairs, $target_bucket, $force)
  1038 + {
  1039 + return self::twoKeyBatch('/move', $source_bucket, $key_pairs, $target_bucket, $force);
  1040 + }
  1041 +
  1042 +
  1043 + public static function buildBatchDelete($bucket, $keys)
  1044 + {
  1045 + return self::oneKeyBatch('/delete', $bucket, $keys);
  1046 + }
  1047 +
  1048 +
  1049 + public static function buildBatchStat($bucket, $keys)
  1050 + {
  1051 + return self::oneKeyBatch('/stat', $bucket, $keys);
  1052 + }
  1053 +
  1054 + public static function buildBatchDeleteAfterDays($bucket, $key_day_pairs)
  1055 + {
  1056 + $data = array();
  1057 + foreach ($key_day_pairs as $key => $day) {
  1058 + array_push($data, '/deleteAfterDays/' . \Qiniu\entry($bucket, $key) . '/' . $day);
  1059 + }
  1060 + return $data;
  1061 + }
  1062 +
  1063 + public static function buildBatchChangeMime($bucket, $key_mime_pairs)
  1064 + {
  1065 + $data = array();
  1066 + foreach ($key_mime_pairs as $key => $mime) {
  1067 + array_push($data, '/chgm/' . \Qiniu\entry($bucket, $key) . '/mime/' . base64_encode($mime));
  1068 + }
  1069 + return $data;
  1070 + }
  1071 +
  1072 + public static function buildBatchChangeType($bucket, $key_type_pairs)
  1073 + {
  1074 + $data = array();
  1075 + foreach ($key_type_pairs as $key => $type) {
  1076 + array_push($data, '/chtype/' . \Qiniu\entry($bucket, $key) . '/type/' . $type);
  1077 + }
  1078 + return $data;
  1079 + }
  1080 +
  1081 + private static function oneKeyBatch($operation, $bucket, $keys)
  1082 + {
  1083 + $data = array();
  1084 + foreach ($keys as $key) {
  1085 + array_push($data, $operation . '/' . \Qiniu\entry($bucket, $key));
  1086 + }
  1087 + return $data;
  1088 + }
  1089 +
  1090 + private static function twoKeyBatch($operation, $source_bucket, $key_pairs, $target_bucket, $force)
  1091 + {
  1092 + if ($target_bucket === null) {
  1093 + $target_bucket = $source_bucket;
  1094 + }
  1095 + $data = array();
  1096 + $forceOp = "false";
  1097 + if ($force) {
  1098 + $forceOp = "true";
  1099 + }
  1100 + foreach ($key_pairs as $from_key => $to_key) {
  1101 + $from = \Qiniu\entry($source_bucket, $from_key);
  1102 + $to = \Qiniu\entry($target_bucket, $to_key);
  1103 + array_push($data, $operation . '/' . $from . '/' . $to . "/force/" . $forceOp);
  1104 + }
  1105 + return $data;
  1106 + }
  1107 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Storage;
  4 +
  5 +use Qiniu\Config;
  6 +use Qiniu\Http\Client;
  7 +use Qiniu\Http\Error;
  8 +
  9 +final class FormUploader
  10 +{
  11 +
  12 + /**
  13 + * 上传二进制流到七牛, 内部使用
  14 + *
  15 + * @param string $upToken 上传凭证
  16 + * @param string $key 上传文件名
  17 + * @param resource $data 上传二进制流
  18 + * @param Config $config 上传配置
  19 + * @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
  20 + * @param string $mime 上传数据的mimeType
  21 + *
  22 + * @return array 包含已上传文件的信息,类似:
  23 + * [
  24 + * "hash" => "<Hash string>",
  25 + * "key" => "<Key string>"
  26 + * ]
  27 + */
  28 + public static function put($upToken, $key, $data, $config, $params, $mime, $fname)
  29 + {
  30 + $fields = array('token' => $upToken);
  31 + if ($key === null) {
  32 + } else {
  33 + $fields['key'] = $key;
  34 + }
  35 +
  36 + //enable crc32 check by default
  37 + $fields['crc32'] = \Qiniu\crc32_data($data);
  38 +
  39 + if ($params) {
  40 + foreach ($params as $k => $v) {
  41 + $fields[$k] = $v;
  42 + }
  43 + }
  44 +
  45 + list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
  46 + if ($err != null) {
  47 + return array(null, $err);
  48 + }
  49 +
  50 + $upHost = $config->getUpHost($accessKey, $bucket);
  51 +
  52 + $response = Client::multipartPost($upHost, $fields, 'file', $fname, $data, $mime);
  53 + if (!$response->ok()) {
  54 + return array(null, new Error($upHost, $response));
  55 + }
  56 + return array($response->json(), null);
  57 + }
  58 +
  59 + /**
  60 + * 上传文件到七牛,内部使用
  61 + *
  62 + * @param string $upToken 上传凭证
  63 + * @param string $key 上传文件名
  64 + * @param string $filePath 上传文件的路径
  65 + * @param Config $config 上传配置
  66 + * @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
  67 + * @param string $mime 上传数据的mimeType
  68 + *
  69 + * @return array 包含已上传文件的信息,类似:
  70 + * [
  71 + * "hash" => "<Hash string>",
  72 + * "key" => "<Key string>"
  73 + * ]
  74 + */
  75 + public static function putFile($upToken, $key, $filePath, $config, $params, $mime)
  76 + {
  77 + $fields = array('token' => $upToken, 'file' => self::createFile($filePath, $mime));
  78 + if ($key !== null) {
  79 + $fields['key'] = $key;
  80 + }
  81 +
  82 + $fields['crc32'] = \Qiniu\crc32_file($filePath);
  83 +
  84 + if ($params) {
  85 + foreach ($params as $k => $v) {
  86 + $fields[$k] = $v;
  87 + }
  88 + }
  89 + $fields['key'] = $key;
  90 + $headers = array('Content-Type' => 'multipart/form-data');
  91 +
  92 + list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
  93 + if ($err != null) {
  94 + return array(null, $err);
  95 + }
  96 +
  97 + $upHost = $config->getUpHost($accessKey, $bucket);
  98 +
  99 + $response = Client::post($upHost, $fields, $headers);
  100 + if (!$response->ok()) {
  101 + return array(null, new Error($upHost, $response));
  102 + }
  103 + return array($response->json(), null);
  104 + }
  105 +
  106 + private static function createFile($filename, $mime)
  107 + {
  108 + // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
  109 + // See: https://wiki.php.net/rfc/curl-file-upload
  110 + if (function_exists('curl_file_create')) {
  111 + return curl_file_create($filename, $mime);
  112 + }
  113 +
  114 + // Use the old style if using an older version of PHP
  115 + $value = "@{$filename}";
  116 + if (!empty($mime)) {
  117 + $value .= ';type=' . $mime;
  118 + }
  119 +
  120 + return $value;
  121 + }
  122 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Storage;
  4 +
  5 +use Qiniu\Config;
  6 +use Qiniu\Http\Client;
  7 +use Qiniu\Http\Error;
  8 +
  9 +/**
  10 + * 断点续上传类, 该类主要实现了断点续上传中的分块上传,
  11 + * 以及相应地创建块和创建文件过程.
  12 + *
  13 + * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
  14 + * @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
  15 + */
  16 +final class ResumeUploader
  17 +{
  18 + private $upToken;
  19 + private $key;
  20 + private $inputStream;
  21 + private $size;
  22 + private $params;
  23 + private $mime;
  24 + private $contexts;
  25 + private $host;
  26 + private $currentUrl;
  27 + private $config;
  28 +
  29 + /**
  30 + * 上传二进制流到七牛
  31 + *
  32 + * @param string $upToken 上传凭证
  33 + * @param string $key 上传文件名
  34 + * @param resource $inputStream 上传二进制流
  35 + * @param int $size 上传流的大小
  36 + * @param array $params 自定义变量
  37 + * @param string $mime 上传数据的mimeType
  38 + *
  39 + * @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
  40 + */
  41 + public function __construct(
  42 + $upToken,
  43 + $key,
  44 + $inputStream,
  45 + $size,
  46 + $params = null,
  47 + $mime = '',
  48 + $config = null
  49 + ) {
  50 +
  51 + $this->upToken = $upToken;
  52 + $this->key = $key;
  53 + $this->inputStream = $inputStream;
  54 + $this->size = $size;
  55 + $this->params = $params;
  56 + $this->mime = $mime ? $mime : 'application/octet-stream';
  57 + $this->contexts = array();
  58 + $this->config = $config ? $config : new Config();
  59 +
  60 + list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
  61 + if ($err != null) {
  62 + return array(null, $err);
  63 + }
  64 +
  65 + $upHost = $this->config->getUpHost($accessKey, $bucket);
  66 + if ($err != null) {
  67 + throw new \Exception($err->message(), 1);
  68 + }
  69 + $this->host = $upHost;
  70 + }
  71 +
  72 + /**
  73 + * 上传操作
  74 + */
  75 + public function upload($fname)
  76 + {
  77 + $uploaded = 0;
  78 + while ($uploaded < $this->size) {
  79 + $blockSize = $this->blockSize($uploaded);
  80 + $data = fread($this->inputStream, $blockSize);
  81 + if ($data === false) {
  82 + throw new \Exception("file read failed", 1);
  83 + }
  84 + $crc = \Qiniu\crc32_data($data);
  85 + $response = $this->makeBlock($data, $blockSize);
  86 + $ret = null;
  87 + if ($response->ok() && $response->json() != null) {
  88 + $ret = $response->json();
  89 + }
  90 + if ($response->statusCode < 0) {
  91 + list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
  92 + if ($err != null) {
  93 + return array(null, $err);
  94 + }
  95 +
  96 + $upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);
  97 + $this->host = $upHostBackup;
  98 + }
  99 + if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
  100 + $response = $this->makeBlock($data, $blockSize);
  101 + $ret = $response->json();
  102 + }
  103 +
  104 + if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
  105 + return array(null, new Error($this->currentUrl, $response));
  106 + }
  107 + array_push($this->contexts, $ret['ctx']);
  108 + $uploaded += $blockSize;
  109 + }
  110 + return $this->makeFile($fname);
  111 + }
  112 +
  113 + public function uploadChunk($index, $file, $size)
  114 + {
  115 + $blockSize = $this->size;
  116 + $data = fread($this->inputStream, $size);
  117 + if ($data === false) {
  118 + throw new \Exception("file read failed", 1);
  119 + }
  120 + $crc = \Qiniu\crc32_data($data);
  121 + $response = $this->makeBlock($data, $blockSize);
  122 + $ret = null;
  123 + if ($response->ok() && $response->json() != null) {
  124 + $ret = $response->json();
  125 + }
  126 + if ($response->statusCode < 0) {
  127 + list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
  128 + if ($err != null) {
  129 + return array(null, $err);
  130 + }
  131 +
  132 + $upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);
  133 + $this->host = $upHostBackup;
  134 + }
  135 + if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
  136 + $response = $this->makeBlock($data, $blockSize);
  137 + $ret = $response->json();
  138 + }
  139 +
  140 + if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
  141 + return array(null, new Error($this->currentUrl, $response));
  142 + }
  143 + array_push($this->contexts, $ret['ctx']);
  144 + return $ret;
  145 + }
  146 +
  147 + public function setContexts($contexts)
  148 + {
  149 + $this->contexts = is_array($contexts) ? $contexts : explode(',', $contexts);
  150 + return $this;
  151 + }
  152 +
  153 + /**
  154 + * 创建块
  155 + */
  156 + private function makeBlock($block, $blockSize)
  157 + {
  158 + $url = $this->host . '/mkblk/' . $blockSize;
  159 + return $this->post($url, $block);
  160 + }
  161 +
  162 + private function fileUrl($fname)
  163 + {
  164 + $url = $this->host . '/mkfile/' . $this->size;
  165 + $url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
  166 + if ($this->key != null) {
  167 + $url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
  168 + }
  169 + $url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);
  170 + if (!empty($this->params)) {
  171 + foreach ($this->params as $key => $value) {
  172 + $val = \Qiniu\base64_urlSafeEncode($value);
  173 + $url .= "/$key/$val";
  174 + }
  175 + }
  176 + return $url;
  177 + }
  178 +
  179 + /**
  180 + * 创建文件
  181 + */
  182 + public function makeFile($fname)
  183 + {
  184 + $url = $this->fileUrl($fname);
  185 + $body = implode(',', $this->contexts);
  186 + $response = $this->post($url, $body);
  187 + if ($response->needRetry()) {
  188 + $response = $this->post($url, $body);
  189 + }
  190 + if (!$response->ok()) {
  191 + return array(null, new Error($this->currentUrl, $response));
  192 + }
  193 + return array($response->json(), null);
  194 + }
  195 +
  196 + private function post($url, $data)
  197 + {
  198 + $this->currentUrl = $url;
  199 + $headers = array('Authorization' => 'UpToken ' . $this->upToken);
  200 + return Client::post($url, $data, $headers);
  201 + }
  202 +
  203 + private function blockSize($uploaded)
  204 + {
  205 + if ($this->size < $uploaded + Config::BLOCK_SIZE) {
  206 + return $this->size - $uploaded;
  207 + }
  208 + return Config::BLOCK_SIZE;
  209 + }
  210 +}
  1 +<?php
  2 +
  3 +namespace Qiniu\Storage;
  4 +
  5 +use Qiniu\Config;
  6 +use Qiniu\Http\HttpClient;
  7 +use Qiniu\Storage\ResumeUploader;
  8 +use Qiniu\Storage\FormUploader;
  9 +
  10 +/**
  11 + * 主要涉及了资源上传接口的实现
  12 + *
  13 + * @link http://developer.qiniu.com/docs/v6/api/reference/up/
  14 + */
  15 +final class UploadManager
  16 +{
  17 + private $config;
  18 +
  19 + public function __construct(Config $config = null)
  20 + {
  21 + if ($config === null) {
  22 + $config = new Config();
  23 + }
  24 + $this->config = $config;
  25 + }
  26 +
  27 + /**
  28 + * 上传二进制流到七牛
  29 + *
  30 + * @param string $upToken 上传凭证
  31 + * @param string $key 上传文件名
  32 + * @param resource $data 上传二进制流
  33 + * @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
  34 + * @param string $mime 上传数据的mimeType
  35 + * @param bool $checkCrc 是否校验crc32
  36 + *
  37 + * @return array 包含已上传文件的信息,类似:
  38 + * [
  39 + * "hash" => "<Hash string>",
  40 + * "key" => "<Key string>"
  41 + * ]
  42 + */
  43 + public function put($upToken, $key, $data, $params = null, $mime = 'application/octet-stream', $fname = "default_filename")
  44 + {
  45 + $params = self::trimParams($params);
  46 + return FormUploader::put($upToken, $key, $data, $this->config, $params, $mime, $fname);
  47 + }
  48 +
  49 +
  50 + /**
  51 + * 上传文件到七牛
  52 + *
  53 + * @param string $upToken 上传凭证
  54 + * @param string $key 上传文件名
  55 + * @param string $filePath 上传文件的路径
  56 + * @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
  57 + * @param string $mime 上传数据的mimeType
  58 + * @param bool $checkCrc 是否校验crc32
  59 + *
  60 + * @return array 包含已上传文件的信息,类似:
  61 + * [
  62 + * "hash" => "<Hash string>",
  63 + * "key" => "<Key string>"
  64 + * ]
  65 + */
  66 + public function putFile($upToken, $key, $filePath, $params = null, $mime = 'application/octet-stream', $checkCrc = false)
  67 + {
  68 + $file = fopen($filePath, 'rb');
  69 + if ($file === false) {
  70 + throw new \Exception("file can not open", 1);
  71 + }
  72 + $params = self::trimParams($params);
  73 + $stat = fstat($file);
  74 + $size = $stat['size'];
  75 + //不满足分片上传条件
  76 + if ($size <= Config::BLOCK_SIZE) {
  77 + $data = fread($file, $size);
  78 + fclose($file);
  79 + if ($data === false) {
  80 + throw new \Exception("file can not read", 1);
  81 + }
  82 + return FormUploader::put($upToken, $key, $data, $this->config, $params, $mime, basename($filePath));
  83 + } else {
  84 + $up = new ResumeUploader($upToken, $key, $file, $size, $params, $mime, $this->config);
  85 + $ret = $up->upload(basename($filePath));
  86 + fclose($file);
  87 + return $ret;
  88 + }
  89 + }
  90 +
  91 + public static function trimParams($params)
  92 + {
  93 + if ($params === null) {
  94 + return null;
  95 + }
  96 + $ret = array();
  97 + foreach ($params as $k => $v) {
  98 + $pos1 = strpos($k, 'x:');
  99 + $pos2 = strpos($k, 'x-qn-meta-');
  100 + if (($pos1 === 0 || $pos2 === 0) && !empty($v)) {
  101 + $ret[$k] = $v;
  102 + }
  103 + }
  104 + return $ret;
  105 + }
  106 +}
  1 +<?php
  2 +namespace Qiniu;
  3 +
  4 +use Qiniu\Region;
  5 +
  6 +class Zone extends Region
  7 +{
  8 + public static function zonez0()
  9 + {
  10 + return parent::regionHuadong();
  11 + }
  12 +
  13 + public static function zonez1()
  14 + {
  15 + return parent::regionHuabei();
  16 + }
  17 +
  18 + public static function zonez2()
  19 + {
  20 + return parent::regionHuanan();
  21 + }
  22 +
  23 + public static function zoneAs0()
  24 + {
  25 + return parent::regionSingapore();
  26 + }
  27 +
  28 + public static function zoneNa0()
  29 + {
  30 + return parent::regionNorthAmerica();
  31 + }
  32 +
  33 + public static function qvmZonez0()
  34 + {
  35 + return parent::qvmRegionHuadong();
  36 + }
  37 +
  38 + public static function qvmZonez1()
  39 + {
  40 + return parent::qvmRegionHuabei();
  41 + }
  42 +
  43 + public static function queryZone($ak, $bucket)
  44 + {
  45 + return parent::queryRegion($ak, $bucket);
  46 + }
  47 +}
  1 +<?php
  2 +
  3 +namespace Qiniu;
  4 +
  5 +use Qiniu\Config;
  6 +
  7 +if (!defined('QINIU_FUNCTIONS_VERSION')) {
  8 + define('QINIU_FUNCTIONS_VERSION', Config::SDK_VER);
  9 +
  10 + /**
  11 + * 计算文件的crc32检验码:
  12 + *
  13 + * @param $file string 待计算校验码的文件路径
  14 + *
  15 + * @return string 文件内容的crc32校验码
  16 + */
  17 + function crc32_file($file)
  18 + {
  19 + $hash = hash_file('crc32b', $file);
  20 + $array = unpack('N', pack('H*', $hash));
  21 + return sprintf('%u', $array[1]);
  22 + }
  23 +
  24 + /**
  25 + * 计算输入流的crc32检验码
  26 + *
  27 + * @param $data 待计算校验码的字符串
  28 + *
  29 + * @return string 输入字符串的crc32校验码
  30 + */
  31 + function crc32_data($data)
  32 + {
  33 + $hash = hash('crc32b', $data);
  34 + $array = unpack('N', pack('H*', $hash));
  35 + return sprintf('%u', $array[1]);
  36 + }
  37 +
  38 + /**
  39 + * 对提供的数据进行urlsafe的base64编码。
  40 + *
  41 + * @param string $data 待编码的数据,一般为字符串
  42 + *
  43 + * @return string 编码后的字符串
  44 + * @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64
  45 + */
  46 + function base64_urlSafeEncode($data)
  47 + {
  48 + $find = array('+', '/');
  49 + $replace = array('-', '_');
  50 + return str_replace($find, $replace, base64_encode($data));
  51 + }
  52 +
  53 + /**
  54 + * 对提供的urlsafe的base64编码的数据进行解码
  55 + *
  56 + * @param string $str 待解码的数据,一般为字符串
  57 + *
  58 + * @return string 解码后的字符串
  59 + */
  60 + function base64_urlSafeDecode($str)
  61 + {
  62 + $find = array('-', '_');
  63 + $replace = array('+', '/');
  64 + return base64_decode(str_replace($find, $replace, $str));
  65 + }
  66 +
  67 + /**
  68 + * Wrapper for JSON decode that implements error detection with helpful
  69 + * error messages.
  70 + *
  71 + * @param string $json JSON data to parse
  72 + * @param bool $assoc When true, returned objects will be converted
  73 + * into associative arrays.
  74 + * @param int $depth User specified recursion depth.
  75 + *
  76 + * @return mixed
  77 + * @throws \InvalidArgumentException if the JSON cannot be parsed.
  78 + * @link http://www.php.net/manual/en/function.json-decode.php
  79 + */
  80 + function json_decode($json, $assoc = false, $depth = 512)
  81 + {
  82 + static $jsonErrors = array(
  83 + JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
  84 + JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
  85 + JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
  86 + JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
  87 + JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
  88 + );
  89 +
  90 + if (empty($json)) {
  91 + return null;
  92 + }
  93 + $data = \json_decode($json, $assoc, $depth);
  94 +
  95 + if (JSON_ERROR_NONE !== json_last_error()) {
  96 + $last = json_last_error();
  97 + throw new \InvalidArgumentException(
  98 + 'Unable to parse JSON data: '
  99 + . (isset($jsonErrors[$last])
  100 + ? $jsonErrors[$last]
  101 + : 'Unknown error')
  102 + );
  103 + }
  104 +
  105 + return $data;
  106 + }
  107 +
  108 + /**
  109 + * 计算七牛API中的数据格式
  110 + *
  111 + * @param $bucket 待操作的空间名
  112 + * @param $key 待操作的文件名
  113 + *
  114 + * @return string 符合七牛API规格的数据格式
  115 + * @link http://developer.qiniu.com/docs/v6/api/reference/data-formats.html
  116 + */
  117 + function entry($bucket, $key)
  118 + {
  119 + $en = $bucket;
  120 + if (!empty($key)) {
  121 + $en = $bucket . ':' . $key;
  122 + }
  123 + return base64_urlSafeEncode($en);
  124 + }
  125 +
  126 + /**
  127 + * array 辅助方法,无值时不set
  128 + *
  129 + * @param $array 待操作array
  130 + * @param $key key
  131 + * @param $value value 为null时 不设置
  132 + *
  133 + * @return array 原来的array,便于连续操作
  134 + */
  135 + function setWithoutEmpty(&$array, $key, $value)
  136 + {
  137 + if (!empty($value)) {
  138 + $array[$key] = $value;
  139 + }
  140 + return $array;
  141 + }
  142 +
  143 + /**
  144 + * 缩略图链接拼接
  145 + *
  146 + * @param string $url 图片链接
  147 + * @param int $mode 缩略模式
  148 + * @param int $width 宽度
  149 + * @param int $height 长度
  150 + * @param string $format 输出类型
  151 + * @param int $quality 图片质量
  152 + * @param int $interlace 是否支持渐进显示
  153 + * @param int $ignoreError 忽略结果
  154 + * @return string
  155 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
  156 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  157 + */
  158 + function thumbnail(
  159 + $url,
  160 + $mode,
  161 + $width,
  162 + $height,
  163 + $format = null,
  164 + $quality = null,
  165 + $interlace = null,
  166 + $ignoreError = 1
  167 + ) {
  168 +
  169 + static $imageUrlBuilder = null;
  170 + if (is_null($imageUrlBuilder)) {
  171 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  172 + }
  173 +
  174 + return call_user_func_array(array($imageUrlBuilder, 'thumbnail'), func_get_args());
  175 + }
  176 +
  177 + /**
  178 + * 图片水印
  179 + *
  180 + * @param string $url 图片链接
  181 + * @param string $image 水印图片链接
  182 + * @param numeric $dissolve 透明度
  183 + * @param string $gravity 水印位置
  184 + * @param numeric $dx 横轴边距
  185 + * @param numeric $dy 纵轴边距
  186 + * @param numeric $watermarkScale 自适应原图的短边比例
  187 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
  188 + * @return string
  189 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  190 + */
  191 + function waterImg(
  192 + $url,
  193 + $image,
  194 + $dissolve = 100,
  195 + $gravity = 'SouthEast',
  196 + $dx = null,
  197 + $dy = null,
  198 + $watermarkScale = null
  199 + ) {
  200 +
  201 + static $imageUrlBuilder = null;
  202 + if (is_null($imageUrlBuilder)) {
  203 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  204 + }
  205 +
  206 + return call_user_func_array(array($imageUrlBuilder, 'waterImg'), func_get_args());
  207 + }
  208 +
  209 + /**
  210 + * 文字水印
  211 + *
  212 + * @param string $url 图片链接
  213 + * @param string $text 文字
  214 + * @param string $font 文字字体
  215 + * @param string $fontSize 文字字号
  216 + * @param string $fontColor 文字颜色
  217 + * @param numeric $dissolve 透明度
  218 + * @param string $gravity 水印位置
  219 + * @param numeric $dx 横轴边距
  220 + * @param numeric $dy 纵轴边距
  221 + * @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
  222 + * @return string
  223 + * @author Sherlock Ren <sherlock_ren@icloud.com>
  224 + */
  225 + function waterText(
  226 + $url,
  227 + $text,
  228 + $font = '黑体',
  229 + $fontSize = 0,
  230 + $fontColor = null,
  231 + $dissolve = 100,
  232 + $gravity = 'SouthEast',
  233 + $dx = null,
  234 + $dy = null
  235 + ) {
  236 +
  237 + static $imageUrlBuilder = null;
  238 + if (is_null($imageUrlBuilder)) {
  239 + $imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
  240 + }
  241 +
  242 + return call_user_func_array(array($imageUrlBuilder, 'waterText'), func_get_args());
  243 + }
  244 +
  245 + /**
  246 + * 从uptoken解析accessKey和bucket
  247 + *
  248 + * @param $upToken
  249 + * @return array(ak,bucket,err=null)
  250 + */
  251 + function explodeUpToken($upToken)
  252 + {
  253 + $items = explode(':', $upToken);
  254 + if (count($items) != 3) {
  255 + return array(null, null, "invalid uptoken");
  256 + }
  257 + $accessKey = $items[0];
  258 + $putPolicy = json_decode(base64_urlSafeDecode($items[2]));
  259 + $scope = $putPolicy->scope;
  260 + $scopeItems = explode(':', $scope);
  261 + $bucket = $scopeItems[0];
  262 + return array($accessKey, $bucket, null);
  263 + }
  264 +}
@@ -11,7 +11,10 @@ date_default_timezone_set('PRC'); @@ -11,7 +11,10 @@ date_default_timezone_set('PRC');
11 11
12 use app\api\controller\getui\JgPush; 12 use app\api\controller\getui\JgPush;
13 use app\common\controller\Api; 13 use app\common\controller\Api;
  14 +use app\common\exception\UploadException;
  15 +use app\common\library\Upload;
14 use fast\Tree; 16 use fast\Tree;
  17 +use think\Config;
15 use think\Db; 18 use think\Db;
16 use think\Request; 19 use think\Request;
17 use Qiniu\Auth as QAuth; 20 use Qiniu\Auth as QAuth;
@@ -93,5 +96,70 @@ class Task extends Api @@ -93,5 +96,70 @@ class Task extends Api
93 } 96 }
94 } 97 }
95 } 98 }
  99 + /**
  100 + * 上传文件
  101 + */
  102 + public function upload()
  103 + {
  104 + Config::set('default_return_type', 'json');
  105 +
  106 + //必须还原upload配置,否则分片及cdnurl函数计算错误
  107 + Config::load(APP_PATH . 'extra/upload.php', 'upload');
  108 +
  109 + $chunkid = $this->request->post("chunkid");
  110 + if ($chunkid) {
  111 + if (!Config::get('upload.chunking')) {
  112 + $this->error(__('Chunk file disabled'));
  113 + }
  114 + $action = $this->request->post("action");
  115 + $chunkindex = $this->request->post("chunkindex/d");
  116 + $chunkcount = $this->request->post("chunkcount/d");
  117 + $filename = $this->request->post("filename");
  118 + $method = $this->request->method(true);
  119 + if ($action == 'merge') {
  120 + $attachment = null;
  121 + //合并分片文件
  122 + try {
  123 + $upload = new Upload();
  124 + $attachment = $upload->merge($chunkid, $chunkcount, $filename);
  125 + } catch (UploadException $e) {
  126 + $this->error($e->getMessage());
  127 + }
  128 + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);
  129 + } elseif ($method == 'clean') {
  130 + //删除冗余的分片文件
  131 + try {
  132 + $upload = new Upload();
  133 + $upload->clean($chunkid);
  134 + } catch (UploadException $e) {
  135 + $this->error($e->getMessage());
  136 + }
  137 + $this->success();
  138 + } else {
  139 + //上传分片文件
  140 + //默认普通上传文件
  141 + $file = $this->request->file('file');
  142 + try {
  143 + $upload = new Upload($file);
  144 + $upload->chunk($chunkid, $chunkindex, $chunkcount);
  145 + } catch (UploadException $e) {
  146 + $this->error($e->getMessage());
  147 + }
  148 + $this->success();
  149 + }
  150 + } else {
  151 + $attachment = null;
  152 + //默认普通上传文件
  153 + $file = $this->request->file('file');
  154 + try {
  155 + $upload = new Upload($file);
  156 + $attachment = $upload->upload();
  157 + } catch (UploadException $e) {
  158 + $this->error($e->getMessage());
  159 + }
  160 +
  161 + $this->success(__('Uploaded successful'), '', ['url' => $attachment->url, 'fullurl' => cdnurl($attachment->url, true)]);
  162 + }
  163 + }
96 164
97 } 165 }
@@ -209,13 +209,11 @@ class Login extends Base @@ -209,13 +209,11 @@ class Login extends Base
209 */ 209 */
210 public function getuserdata(){ 210 public function getuserdata(){
211 $username=$this->request->param("username"); 211 $username=$this->request->param("username");
212 - $avatar=$this->request->param("avatar");  
213 $user_id=$this->auth->id; 212 $user_id=$this->auth->id;
214 Db::name("user") 213 Db::name("user")
215 ->where("id", $user_id) 214 ->where("id", $user_id)
216 ->update([ 215 ->update([
217 "username" => $username, 216 "username" => $username,
218 - "avatar" => $avatar,  
219 "nickname" => $username, 217 "nickname" => $username,
220 ]); 218 ]);
221 $this->success("请求成功"); 219 $this->success("请求成功");