作者 李忠强

更新

要显示太多修改。

为保证性能只显示 27 of 27+ 个文件。

{"files":[],"license":"basic","licenseto":"10789","licensekey":"vkcmEG5rnXOZuhj2 h5tINZp9pykKJBADNEQ3lw==","domains":[],"licensecodes":[],"validations":[]}
\ No newline at end of file
... ...
<?php
namespace addons\qiniu;
use fast\Http;
use Qiniu\Auth;
use think\Addons;
use think\Loader;
/**
* 七牛云储存插件
*/
class Qiniu extends Addons
{
/**
* 插件安装方法
* @return bool
*/
public function install()
{
return true;
}
/**
* 插件卸载方法
* @return bool
*/
public function uninstall()
{
return true;
}
/**
* 上传初始化时
*/
public function uploadConfigInit(&$upload)
{
$config = $this->getConfig();
$policy = array(
'saveKey' => ltrim($config['savekey'], '/'),
);
$config['savekey'] = str_replace(['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'], ['{year}', '{mon}', '{day}', '{filemd5}', '{.suffix}'], $config['savekey']);
$auth = new Auth($config['accessKey'], $config['secretKey']);
$multipart['qiniutoken'] = $auth->uploadToken($config['bucket'], null, $config['expire'], $policy);
$upload = array_merge($upload, [
'cdnurl' => $config['cdnurl'],
'uploadurl' => $config['uploadmode'] == 'client' ? $config['uploadurl'] : addon_url('qiniu/index/upload', [], false, true),
'uploadmode' => $config['uploadmode'],
'bucket' => $config['bucket'],
'maxsize' => $config['maxsize'],
'mimetype' => $config['mimetype'],
'savekey' => $config['savekey'],
'chunking' => (bool)($config['chunking'] ?? $upload['chunking']),
'chunksize' => (int)($config['chunksize'] ?? $upload['chunksize']),
'multipart' => $multipart,
'storage' => $this->getName(),
'multiple' => $config['multiple'] ? true : false,
]);
}
/**
* 附件删除后
*/
public function uploadDelete($attachment)
{
$config = $this->getConfig();
if ($attachment['storage'] == 'qiniu' && isset($config['syncdelete']) && $config['syncdelete']) {
$auth = new Auth($config['accessKey'], $config['secretKey']);
$entry = $config['bucket'] . ':' . ltrim($attachment->url, '/');
$encodedEntryURI = \Qiniu\base64_urlSafeEncode($entry);
$url = 'http://rs.qiniu.com/delete/' . $encodedEntryURI;
$headers = $auth->authorization($url);
//删除云储存文件
$ret = Http::sendRequest($url, [], 'POST', [CURLOPT_HTTPHEADER => ['Authorization: ' . $headers['Authorization']]]);
//如果是服务端中转,还需要删除本地文件
//if ($config['uploadmode'] == 'server') {
// $filePath = ROOT_PATH . 'public' . str_replace('/', DS, $attachment->url);
// if ($filePath) {
// @unlink($filePath);
// }
//}
}
return true;
}
public function appInit()
{
if (!class_exists('\Qiniu\Config')) {
Loader::addNamespace('Qiniu', ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS);
require_once ADDON_PATH . 'qiniu' . DS . 'library' . DS . 'Qiniu' . DS . 'functions.php';
}
}
}
... ...
//修改上传的接口调用
require(['upload'], function (Upload) {
//初始化中完成判断
Upload.events.onInit = function () {
//如果上传接口不是七牛云,则不处理
if (this.options.url !== Config.upload.uploadurl) {
return;
}
var _success = this.options.success;
$.extend(this.options, {
chunkSuccess: function (chunk, file, response) {
this.contexts = this.contexts ? this.contexts : [];
this.contexts.push(typeof response.ctx !== 'undefined' ? response.ctx : response.data.ctx);
},
chunksUploaded: function (file, done) {
var that = this;
var params = $(that.element).data("params") || {};
var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
Fast.api.ajax({
url: "/addons/qiniu/index/upload",
data: {
action: 'merge',
filesize: file.size,
filename: file.name,
chunkid: file.upload.uuid,
chunkcount: file.upload.totalChunkCount,
width: file.width || 0,
height: file.height || 0,
type: file.type,
category: category,
qiniutoken: Config.upload.multipart.qiniutoken,
contexts: this.contexts
},
}, function (data, ret) {
done(JSON.stringify(ret));
return false;
}, function (data, ret) {
file.accepted = false;
that._errorProcessing([file], ret.msg);
return false;
});
},
});
//先移除已有的事件
this.off("success", _success).on("success", function (file, response) {
var that = this;
var ret = {code: 0, msg: response};
try {
ret = typeof response === 'string' ? JSON.parse(response) : response;
if (file.xhr.status === 200) {
if (typeof ret.key !== 'undefined') {
ret = {code: 1, msg: "", data: {url: '/' + ret.key, hash: ret.hash}};
}
console.log(ret);
var params = $(that.element).data("params") || {};
var category = typeof params.category !== 'undefined' ? params.category : ($(that.element).data("category") || '');
Fast.api.ajax({
url: "/addons/qiniu/index/notify",
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}
}, function () {
return false;
}, function () {
return false;
});
}
} catch (e) {
console.error(e);
}
_success.call(this, file, ret);
});
//如果是直传模式
if (Config.upload.uploadmode === 'client') {
var _url = this.options.url;
//分片上传时URL链接不同
this.options.url = function (files) {
this.options.headers = {"Authorization": "UpToken " + Config.upload.multipart.qiniutoken};
if (files[0].upload.chunked) {
var chunk = null;
files[0].upload.chunks.forEach(function (item) {
if (item.status === 'uploading') {
chunk = item;
}
});
if (!chunk) {
return Config.upload.uploadurl + '/mkfile/' + files[0].size;
} else {
return Config.upload.uploadurl + '/mkblk/' + chunk.dataBlock.data.size;
}
}
return _url;
};
this.options.params = function (files, xhr, chunk) {
var params = Config.upload.multipart;
if (chunk) {
return $.extend({}, params, {
filesize: chunk.file.size,
filename: chunk.file.name,
chunkid: chunk.file.upload.uuid,
chunkindex: chunk.index,
chunkcount: chunk.file.upload.totalChunkCount,
chunkfilesize: chunk.dataBlock.data.size,
width: chunk.file.width || 0,
height: chunk.file.height || 0,
type: chunk.file.type,
});
} else {
var retParams = $.extend({}, params);
//七牛云直传使用的是token参数
retParams.token = retParams.qiniutoken;
delete retParams.qiniutoken;
return retParams;
}
};
//分片上传时需要变更提交的内容
this.on("sending", function (file, xhr, formData) {
if (file.upload.chunked) {
var _send = xhr.send;
xhr.send = function () {
var chunk = null;
file.upload.chunks.forEach(function (item) {
if (item.status == 'uploading') {
chunk = item;
}
});
if (chunk) {
_send.call(xhr, chunk.dataBlock.data);
}
};
}
});
}
};
});
... ...
<?php
return array(
array(
'name' => 'accessKey',
'title' => 'accessKey',
'type' => 'string',
'content' =>
array(),
'value' => '',
'rule' => 'required',
'msg' => '',
'tip' => '请在个人中心 > 密钥管理中获取 > AK',
'ok' => '',
'extend' => '',
),
array(
'name' => 'secretKey',
'title' => 'secretKey',
'type' => 'string',
'content' =>
array(),
'value' => '',
'rule' => 'required',
'msg' => '',
'tip' => '请在个人中心 > 密钥管理中获取 > SK',
'ok' => '',
'extend' => '',
),
array(
'name' => 'bucket',
'title' => 'bucket',
'type' => 'string',
'content' =>
array(),
'value' => 'yourbucket',
'rule' => 'required',
'msg' => '',
'tip' => '存储空间名称',
'ok' => '',
'extend' => '',
),
array(
'name' => 'uploadurl',
'title' => '上传接口地址',
'type' => 'select',
'content' =>
array(
'https://upload-z0.qiniup.com' => '华东 https://upload-z0.qiniup.com',
'https://upload-z1.qiniup.com' => '华北 https://upload-z1.qiniup.com',
'https://upload-z2.qiniup.com' => '华南 https://upload-z2.qiniup.com',
'https://upload-na0.qiniup.com' => '北美 https://upload-na0.qiniup.com',
'https://upload-as0.qiniup.com' => '东南亚 https://upload-as0.qiniup.com',
'https://up-cn-east-2.qiniup.com' => 'https://up-cn-east-2.qiniup.com',
),
'value' => 'https://upload-z2.qiniup.com',
'rule' => 'required',
'msg' => '',
'tip' => '推荐选择最近的地址',
'ok' => '',
'extend' => '',
),
array(
'name' => 'cdnurl',
'title' => 'CDN地址',
'type' => 'string',
'content' =>
array(),
'value' => 'http://yourbucket.yoursite.com',
'rule' => 'required',
'msg' => '',
'tip' => '未绑定CDN的话可使用七牛分配的测试域名',
'ok' => '',
'extend' => '',
),
array(
'name' => 'uploadmode',
'title' => '上传模式',
'type' => 'select',
'content' =>
array(
'client' => '客户端直传(速度快,无备份)',
'server' => '服务器中转(占用服务器带宽,可备份)',
),
'value' => 'server',
'rule' => '',
'msg' => '',
'tip' => '启用服务器中转时务必配置操作员和密码',
'ok' => '',
'extend' => '',
),
[
'name' => 'serverbackup',
'title' => '服务器中转模式备份',
'type' => 'radio',
'content' => [
1 => '备份(附件管理将产生2条记录)',
0 => '不备份',
],
'value' => '1',
'rule' => '',
'msg' => '',
'tip' => '服务器中转模式下是否备份文件',
'ok' => '',
'extend' => '',
],
array(
'name' => 'savekey',
'title' => '保存文件名',
'type' => 'string',
'content' =>
array(),
'value' => '/uploads/$(year)$(mon)$(day)/$(etag)$(ext)',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'expire',
'title' => '上传有效时长',
'type' => 'string',
'content' =>
array(),
'value' => '600',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'maxsize',
'title' => '最大可上传',
'type' => 'string',
'content' =>
array(),
'value' => '10M',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'mimetype',
'title' => '可上传后缀格式',
'type' => 'string',
'content' =>
array(),
'value' => 'jpg,png,bmp,jpeg,gif,zip,rar,xls,xlsx',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'multiple',
'title' => '多文件上传',
'type' => 'radio',
'content' =>
array(
'1' => '开启',
'0' => '关闭',
),
'value' => '0',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'thumbstyle',
'title' => '缩略图样式',
'type' => 'string',
'content' =>
array(),
'value' => '',
'rule' => '',
'msg' => '',
'tip' => '用于附件管理缩略图样式,可使用:?imageView2/2/w/120/h/90/q/80',
'ok' => '',
'extend' => '',
),
array(
'name' => 'chunking',
'title' => '分片上传',
'type' => 'radio',
'content' =>
array(
'1' => '开启',
'0' => '关闭',
),
'value' => '0',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
),
array(
'name' => 'chunksize',
'title' => '分片大小',
'type' => 'number',
'content' =>
array(),
'value' => '4194304',
'rule' => 'required',
'msg' => '',
'tip' => '固定大小,不能修改',
'ok' => '',
'extend' => 'readonly',
),
array(
'name' => 'syncdelete',
'title' => '附件删除时是否同步删除文件',
'type' => 'bool',
'content' =>
array(),
'value' => '0',
'rule' => 'required',
'msg' => '',
'tip' => '',
'ok' => '',
'extend' => '',
)
);
... ...
<?php
namespace addons\qiniu\controller;
use app\common\exception\UploadException;
use app\common\library\Upload;
use app\common\model\Attachment;
use Qiniu\Auth;
use Qiniu\Storage\ResumeUploader;
use Qiniu\Storage\UploadManager;
use think\addons\Controller;
use think\Config;
/**
* 七牛管理
*
*/
class Index extends Controller
{
public function _initialize()
{
//跨域检测
check_cors_request();
parent::_initialize();
Config::set('default_return_type', 'json');
}
public function index()
{
Config::set('default_return_type', 'html');
$this->error("当前插件暂无前台页面");
}
/**
* 中转上传文件
* 上传分片
* 合并分片
*/
public function upload()
{
Config::set('default_return_type', 'json');
$this->check();
$config = get_addon_config('qiniu');
$config['savekey'] = str_replace(['{year}', '{mon}', '{day}', '{filemd5}', '{.suffix}'], ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'], $config['savekey']);
// 构建鉴权对象
$auth = new Auth($config['accessKey'], $config['secretKey']);
// 生成上传 Token
$token = $auth->uploadToken($config['bucket'], null, 3600, ['saveKey' => ltrim($config['savekey'], '/')]);
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
//检测删除文件或附件
$checkDeleteFile = function ($attachment, $upload, $force = false) use ($config) {
//如果设定为不备份则删除文件和记录 或 强制删除
if ((isset($config['serverbackup']) && !$config['serverbackup']) || $force) {
if ($attachment && !empty($attachment['id'])) {
$attachment->delete();
}
if ($upload) {
//文件绝对路径
$filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
@unlink($filePath);
}
}
};
$chunkid = $this->request->post("chunkid");
if ($chunkid) {
$action = $this->request->post("action");
$chunkindex = $this->request->post("chunkindex/d");
$chunkcount = $this->request->post("chunkcount/d");
$filesize = $this->request->post("filesize");
$filename = $this->request->post("filename");
if ($action == 'merge') {
$attachment = null;
$upload = null;
if ($config['uploadmode'] == 'server') {
//合并分片文件
try {
$upload = new Upload();
$attachment = $upload->merge($chunkid, $chunkcount, $filename);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
}
$contexts = $this->request->post("contexts/a", []);
$uploader = new ResumeUploader($token, null, null, $filesize);
list($ret, $err) = $uploader->setContexts($contexts)->makeFile($filename);
if ($err !== null) {
$checkDeleteFile($attachment, $upload, true);
$this->error("上传失败");
} else {
$checkDeleteFile($attachment, $upload);
$this->success("上传成功", '', ['url' => '/' . $ret['key'], 'fullurl' => cdnurl('/' . $ret['key'], true), 'hash' => $ret['hash']]);
}
} else {
//默认普通上传文件
$file = $this->request->file('file');
try {
$upload = new Upload($file);
$file = $upload->chunk($chunkid, $chunkindex, $chunkcount);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
//上传分片文件
//$file = $this->request->file('file');
$filesize = $file->getSize();
//合并分片文件
$uploader = new ResumeUploader($token, null, fopen($file->getRealPath(), 'rb'), $filesize);
$ret = $uploader->uploadChunk($chunkindex, $file, $filesize);
$this->success("上传成功", "", $ret);
}
} else {
$attachment = null;
//默认普通上传文件
$file = $this->request->file('file');
try {
$upload = new Upload($file);
$suffix = $upload->getSuffix();
$md5 = md5_file($file->getRealPath());
$search = ['$(year)', '$(mon)', '$(day)', '$(etag)', '$(ext)'];
$replace = [date("Y"), date("m"), date("d"), $md5, '.' . $suffix];
$savekey = ltrim(str_replace($search, $replace, $config['savekey']), '/');
$attachment = $upload->upload($savekey);
} catch (UploadException $e) {
$this->error($e->getMessage());
}
//文件绝对路径
$filePath = $upload->getFile()->getRealPath() ?: $upload->getFile()->getPathname();
//上传到七牛后保存的文件名
$saveKey = ltrim($attachment->url, '/');
$url = $attachment->url;
try {
// 调用 UploadManager 的 putFile 方法进行文件的上传。
list($ret, $err) = $uploadMgr->putFile($token, $saveKey, $filePath);
if ($err !== null) {
throw new \Exception("上传失败");
}
//成功不做任何操作
} catch (\Exception $e) {
$checkDeleteFile($attachment, $upload, true);
$this->error("上传失败");
}
$hash = md5_file($filePath);
$checkDeleteFile($attachment, $upload);
$this->success("上传成功", '', ['url' => $url, 'fullurl' => cdnurl($url, true), 'hash' => $hash]);
}
}
/**
* 通知回调
*/
public function notify()
{
Config::set('default_return_type', 'json');
$this->check();
$size = $this->request->post('size/d');
$name = $this->request->post('name', '');
$hash = $this->request->post('hash', '');
$type = $this->request->post('type', '');
$url = $this->request->post('url', '');
$width = $this->request->post('width/d');
$height = $this->request->post('height/d');
$category = $this->request->post('category', '');
$suffix = substr($name, stripos($name, '.') + 1);
$attachment = Attachment::where('url', $url)->where('storage', 'qiniu')->find();
if (!$attachment) {
$params = array(
'category' => $category,
'admin_id' => (int)session('admin.id'),
'user_id' => (int)cookie('uid'),
'filename' => $name,
'filesize' => $size,
'imagewidth' => $width,
'imageheight' => $height,
'imagetype' => $suffix,
'imageframes' => 0,
'mimetype' => $type,
'url' => $url,
'uploadtime' => time(),
'storage' => 'qiniu',
'sha1' => $hash,
);
Attachment::create($params);
}
$this->success();
}
/**
* 检查签名是否正确或过期
*/
protected function check()
{
$qiniutoken = $this->request->post('qiniutoken', $this->request->server('AUTHORIZATION'), 'trim');
if (!$qiniutoken) {
$this->error("参数不正确");
}
$config = get_addon_config('qiniu');
$auth = new Auth($config['accessKey'], $config['secretKey']);
list($accessKey, $sign, $data) = explode(':', $qiniutoken);
if (!$accessKey || !$sign || !$data) {
$this->error("参数不正确");
}
if ($accessKey !== $config['accessKey']) {
$this->error("参数不正确");
}
if ($accessKey . ':' . $sign !== $auth->sign($data)) {
$this->error("签名不正确");
}
$json = json_decode(\Qiniu\base64_urlSafeDecode($data), true);
if ($json['deadline'] < time()) {
$this->error("请求已经超时");
}
}
}
... ...
name = qiniu
title = 七牛云储存
intro = 使用七牛云储存,支持直传、服务器中转、分片上传
author = FastAdmin
website = https://www.fastadmin.net
version = 1.1.5
state = 1
url = /addons/qiniu
license = basic
licenseto = 10789
... ...
<?php
namespace Qiniu;
use Qiniu\Zone;
final class Auth
{
private $accessKey;
private $secretKey;
public function __construct($accessKey, $secretKey)
{
$this->accessKey = $accessKey;
$this->secretKey = $secretKey;
}
public function getAccessKey()
{
return $this->accessKey;
}
public function sign($data)
{
$hmac = hash_hmac('sha1', $data, $this->secretKey, true);
return $this->accessKey . ':' . \Qiniu\base64_urlSafeEncode($hmac);
}
public function signWithData($data)
{
$encodedData = \Qiniu\base64_urlSafeEncode($data);
return $this->sign($encodedData) . ':' . $encodedData;
}
public function signRequest($urlString, $body, $contentType = null)
{
$url = parse_url($urlString);
$data = '';
if (array_key_exists('path', $url)) {
$data = $url['path'];
}
if (array_key_exists('query', $url)) {
$data .= '?' . $url['query'];
}
$data .= "\n";
if ($body !== null && $contentType === 'application/x-www-form-urlencoded') {
$data .= $body;
}
return $this->sign($data);
}
public function verifyCallback($contentType, $originAuthorization, $url, $body)
{
$authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
return $originAuthorization === $authorization;
}
public function privateDownloadUrl($baseUrl, $expires = 3600)
{
$deadline = time() + $expires;
$pos = strpos($baseUrl, '?');
if ($pos !== false) {
$baseUrl .= '&e=';
} else {
$baseUrl .= '?e=';
}
$baseUrl .= $deadline;
$token = $this->sign($baseUrl);
return "$baseUrl&token=$token";
}
public function uploadToken($bucket, $key = null, $expires = 3600, $policy = null, $strictPolicy = true)
{
$deadline = time() + $expires;
$scope = $bucket;
if ($key !== null) {
$scope .= ':' . $key;
}
$args = self::copyPolicy($args, $policy, $strictPolicy);
$args['scope'] = $scope;
$args['deadline'] = $deadline;
$b = json_encode($args);
return $this->signWithData($b);
}
/**
*上传策略,参数规格详见
*http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html
*/
private static $policyFields = array(
'callbackUrl',
'callbackBody',
'callbackHost',
'callbackBodyType',
'callbackFetchKey',
'returnUrl',
'returnBody',
'endUser',
'saveKey',
'insertOnly',
'detectMime',
'mimeLimit',
'fsizeMin',
'fsizeLimit',
'persistentOps',
'persistentNotifyUrl',
'persistentPipeline',
'deleteAfterDays',
'fileType',
'isPrefixalScope',
);
private static function copyPolicy(&$policy, $originPolicy, $strictPolicy)
{
if ($originPolicy === null) {
return array();
}
foreach ($originPolicy as $key => $value) {
if (!$strictPolicy || in_array((string)$key, self::$policyFields, true)) {
$policy[$key] = $value;
}
}
return $policy;
}
public function authorization($url, $body = null, $contentType = null)
{
$authorization = 'QBox ' . $this->signRequest($url, $body, $contentType);
return array('Authorization' => $authorization);
}
public function authorizationV2($url, $method, $body = null, $contentType = null)
{
$urlItems = parse_url($url);
$host = $urlItems['host'];
if (isset($urlItems['port'])) {
$port = $urlItems['port'];
} else {
$port = '';
}
$path = $urlItems['path'];
if (isset($urlItems['query'])) {
$query = $urlItems['query'];
} else {
$query = '';
}
//write request uri
$toSignStr = $method . ' ' . $path;
if (!empty($query)) {
$toSignStr .= '?' . $query;
}
//write host and port
$toSignStr .= "\nHost: " . $host;
if (!empty($port)) {
$toSignStr .= ":" . $port;
}
//write content type
if (!empty($contentType)) {
$toSignStr .= "\nContent-Type: " . $contentType;
}
$toSignStr .= "\n\n";
//write body
if (!empty($body)) {
$toSignStr .= $body;
}
$sign = $this->sign($toSignStr);
$auth = 'Qiniu ' . $sign;
return array('Authorization' => $auth);
}
}
... ...
<?php
namespace Qiniu\Cdn;
use Qiniu\Auth;
use Qiniu\Http\Error;
use Qiniu\Http\Client;
final class CdnManager
{
private $auth;
private $server;
public function __construct(Auth $auth)
{
$this->auth = $auth;
$this->server = 'http://fusion.qiniuapi.com';
}
/**
* @param array $urls 待刷新的文件链接数组
* @return array
*/
public function refreshUrls(array $urls)
{
return $this->refreshUrlsAndDirs($urls, array());
}
/**
* @param array $dirs 待刷新的文件链接数组
* @return array
* 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
* 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
*/
public function refreshDirs(array $dirs)
{
return $this->refreshUrlsAndDirs(array(), $dirs);
}
/**
* @param array $urls 待刷新的文件链接数组
* @param array $dirs 待刷新的目录链接数组
*
* @return array 刷新的请求回复和错误,参考 examples/cdn_manager.php 代码
* @link http://developer.qiniu.com/article/fusion/api/refresh.html
*
* 目前客户默认没有目录刷新权限,刷新会有400038报错,参考:https://developer.qiniu.com/fusion/api/1229/cache-refresh
* 需要刷新目录请工单联系技术支持 https://support.qiniu.com/tickets/category
*/
public function refreshUrlsAndDirs(array $urls, array $dirs)
{
$req = array();
if (!empty($urls)) {
$req['urls'] = $urls;
}
if (!empty($dirs)) {
$req['dirs'] = $dirs;
}
$url = $this->server . '/v2/tune/refresh';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $urls 待预取的文件链接数组
*
* @return array 预取的请求回复和错误,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/refresh.html
*/
public function prefetchUrls(array $urls)
{
$req = array(
'urls' => $urls,
);
$url = $this->server . '/v2/tune/prefetch';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $domains 待获取带宽数据的域名数组
* @param string $startDate 开始的日期,格式类似 2017-01-01
* @param string $endDate 结束的日期,格式类似 2017-01-01
* @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
*
* @return array 带宽数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
*/
public function getBandwidthData(array $domains, $startDate, $endDate, $granularity)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['startDate'] = $startDate;
$req['endDate'] = $endDate;
$req['granularity'] = $granularity;
$url = $this->server . '/v2/tune/bandwidth';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $domains 待获取流量数据的域名数组
* @param string $startDate 开始的日期,格式类似 2017-01-01
* @param string $endDate 结束的日期,格式类似 2017-01-01
* @param string $granularity 获取数据的时间间隔,可以是 5min, hour 或者 day
*
* @return array 流量数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/traffic-bandwidth.html
*/
public function getFluxData(array $domains, $startDate, $endDate, $granularity)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['startDate'] = $startDate;
$req['endDate'] = $endDate;
$req['granularity'] = $granularity;
$url = $this->server . '/v2/tune/flux';
$body = json_encode($req);
return $this->post($url, $body);
}
/**
* @param array $domains 待获取日志下载链接的域名数组
* @param string $logDate 获取指定日期的日志下载链接,格式类似 2017-01-01
*
* @return array 日志下载链接数据和错误信息,参考 examples/cdn_manager.php 代码
*
* @link http://developer.qiniu.com/article/fusion/api/log.html
*/
public function getCdnLogList(array $domains, $logDate)
{
$req = array();
$req['domains'] = implode(';', $domains);
$req['day'] = $logDate;
$url = $this->server . '/v2/tune/log/list';
$body = json_encode($req);
return $this->post($url, $body);
}
private function post($url, $body)
{
$headers = $this->auth->authorization($url, $body, 'application/json');
$headers['Content-Type'] = 'application/json';
$ret = Client::post($url, $body, $headers);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
/**
* 构建时间戳防盗链鉴权的访问外链
*
* @param string $rawUrl 需要签名的资源url
* @param string $encryptKey 时间戳防盗链密钥
* @param string $durationInSeconds 链接的有效期(以秒为单位)
*
* @return string 带鉴权信息的资源外链,参考 examples/cdn_timestamp_antileech.php 代码
*/
public static function createTimestampAntiLeechUrl($rawUrl, $encryptKey, $durationInSeconds)
{
$parsedUrl = parse_url($rawUrl);
$deadline = time() + $durationInSeconds;
$expireHex = dechex($deadline);
$path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
$strToSign = $encryptKey . $path . $expireHex;
$signStr = md5($strToSign);
if (isset($parsedUrl['query'])) {
$signedUrl = $rawUrl . '&sign=' . $signStr . '&t=' . $expireHex;
} else {
$signedUrl = $rawUrl . '?sign=' . $signStr . '&t=' . $expireHex;
}
return $signedUrl;
}
}
... ...
<?php
namespace Qiniu;
final class Config
{
const SDK_VER = '7.2.10';
const BLOCK_SIZE = 4194304; //4*1024*1024 分块上传块大小,该参数为接口规格,不能修改
const RSF_HOST = 'rsf.qiniu.com';
const API_HOST = 'api.qiniu.com';
const RS_HOST = 'rs.qiniu.com'; //RS Host
const UC_HOST = 'uc.qbox.me'; //UC Host
const RTCAPI_HOST = 'http://rtc.qiniuapi.com';
const ARGUS_HOST = 'argus.atlab.ai';
const CASTER_HOST = 'pili-caster.qiniuapi.com';
const SMS_HOST="https://sms.qiniuapi.com";
const RTCAPI_VERSION = 'v3';
const SMS_VERSION='v1';
// Zone 空间对应的存储区域
public $region;
//BOOL 是否使用https域名
public $useHTTPS;
//BOOL 是否使用CDN加速上传域名
public $useCdnDomains;
// Zone Cache
private $regionCache;
// 构造函数
public function __construct(Region $z = null)
{
$this->zone = $z;
$this->useHTTPS = false;
$this->useCdnDomains = false;
$this->regionCache = array();
}
public function getUpHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->srcUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->cdnUpHosts[0];
}
return $scheme . $host;
}
public function getUpBackupHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
$host = $region->cdnUpHosts[0];
if ($this->useCdnDomains === true) {
$host = $region->srcUpHosts[0];
}
return $scheme . $host;
}
public function getRsHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->rsHost;
}
public function getRsfHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->rsfHost;
}
public function getIovipHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->iovipHost;
}
public function getApiHost($accessKey, $bucket)
{
$region = $this->getRegion($accessKey, $bucket);
if ($this->useHTTPS === true) {
$scheme = "https://";
} else {
$scheme = "http://";
}
return $scheme . $region->apiHost;
}
private function getRegion($accessKey, $bucket)
{
$cacheId = "$accessKey:$bucket";
if (isset($this->regionCache[$cacheId])) {
$region = $this->regionCache[$cacheId];
} elseif (isset($this->zone)) {
$region = $this->zone;
$this->regionCache[$cacheId] = $region;
} else {
$region = Zone::queryZone($accessKey, $bucket);
$this->regionCache[$cacheId] = $region;
}
return $region;
}
}
... ...
<?php
namespace Qiniu;
use Qiniu\Config;
final class Etag
{
private static function packArray($v, $a)
{
return call_user_func_array('pack', array_merge(array($v), (array)$a));
}
private static function blockCount($fsize)
{
return intval(($fsize + (Config::BLOCK_SIZE - 1)) / Config::BLOCK_SIZE);
}
private static function calcSha1($data)
{
$sha1Str = sha1($data, true);
$err = error_get_last();
if ($err !== null) {
return array(null, $err);
}
$byteArray = unpack('C*', $sha1Str);
return array($byteArray, null);
}
public static function sum($filename)
{
$fhandler = fopen($filename, 'r');
$err = error_get_last();
if ($err !== null) {
return array(null, $err);
}
$fstat = fstat($fhandler);
$fsize = $fstat['size'];
if ((int)$fsize === 0) {
fclose($fhandler);
return array('Fto5o-5ea0sNMlW_75VgGJCv2AcJ', null);
}
$blockCnt = self::blockCount($fsize);
$sha1Buf = array();
if ($blockCnt <= 1) {
array_push($sha1Buf, 0x16);
$fdata = fread($fhandler, Config::BLOCK_SIZE);
if ($err !== null) {
fclose($fhandler);
return array(null, $err);
}
list($sha1Code,) = self::calcSha1($fdata);
$sha1Buf = array_merge($sha1Buf, $sha1Code);
} else {
array_push($sha1Buf, 0x96);
$sha1BlockBuf = array();
for ($i = 0; $i < $blockCnt; $i++) {
$fdata = fread($fhandler, Config::BLOCK_SIZE);
list($sha1Code, $err) = self::calcSha1($fdata);
if ($err !== null) {
fclose($fhandler);
return array(null, $err);
}
$sha1BlockBuf = array_merge($sha1BlockBuf, $sha1Code);
}
$tmpData = self::packArray('C*', $sha1BlockBuf);
list($sha1Final,) = self::calcSha1($tmpData);
$sha1Buf = array_merge($sha1Buf, $sha1Final);
}
$etag = \Qiniu\base64_urlSafeEncode(self::packArray('C*', $sha1Buf));
return array($etag, null);
}
}
... ...
<?php
namespace Qiniu\Http;
use Qiniu\Config;
use Qiniu\Http\Request;
use Qiniu\Http\Response;
final class Client
{
public static function get($url, array $headers = array())
{
$request = new Request('GET', $url, $headers);
return self::sendRequest($request);
}
public static function delete($url, array $headers = array())
{
$request = new Request('DELETE', $url, $headers);
return self::sendRequest($request);
}
public static function post($url, $body, array $headers = array())
{
$request = new Request('POST', $url, $headers, $body);
return self::sendRequest($request);
}
public static function PUT($url, $body, array $headers = array())
{
$request = new Request('PUT', $url, $headers, $body);
return self::sendRequest($request);
}
public static function multipartPost(
$url,
$fields,
$name,
$fileName,
$fileBody,
$mimeType = null,
array $headers = array()
) {
$data = array();
$mimeBoundary = md5(microtime());
foreach ($fields as $key => $val) {
array_push($data, '--' . $mimeBoundary);
array_push($data, "Content-Disposition: form-data; name=\"$key\"");
array_push($data, '');
array_push($data, $val);
}
array_push($data, '--' . $mimeBoundary);
$finalMimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType;
$finalFileName = self::escapeQuotes($fileName);
array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$finalFileName\"");
array_push($data, "Content-Type: $finalMimeType");
array_push($data, '');
array_push($data, $fileBody);
array_push($data, '--' . $mimeBoundary . '--');
array_push($data, '');
$body = implode("\r\n", $data);
// var_dump($data);exit;
$contentType = 'multipart/form-data; boundary=' . $mimeBoundary;
$headers['Content-Type'] = $contentType;
$request = new Request('POST', $url, $headers, $body);
return self::sendRequest($request);
}
private static function userAgent()
{
$sdkInfo = "QiniuPHP/" . Config::SDK_VER;
$systemInfo = php_uname("s");
$machineInfo = php_uname("m");
$envInfo = "($systemInfo/$machineInfo)";
$phpVer = phpversion();
$ua = "$sdkInfo $envInfo PHP/$phpVer";
return $ua;
}
public static function sendRequest($request)
{
$t1 = microtime(true);
$ch = curl_init();
$options = array(
CURLOPT_USERAGENT => self::userAgent(),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => false,
CURLOPT_CUSTOMREQUEST => $request->method,
CURLOPT_URL => $request->url,
);
// Handle open_basedir & safe mode
if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
$options[CURLOPT_FOLLOWLOCATION] = true;
}
if (!empty($request->headers)) {
$headers = array();
foreach ($request->headers as $key => $val) {
array_push($headers, "$key: $val");
}
$options[CURLOPT_HTTPHEADER] = $headers;
}
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
if (!empty($request->body)) {
$options[CURLOPT_POSTFIELDS] = $request->body;
}
curl_setopt_array($ch, $options);
$result = curl_exec($ch);
$t2 = microtime(true);
$duration = round($t2 - $t1, 3);
$ret = curl_errno($ch);
if ($ret !== 0) {
$r = new Response(-1, $duration, array(), null, curl_error($ch));
curl_close($ch);
return $r;
}
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = self::parseHeaders(substr($result, 0, $header_size));
$body = substr($result, $header_size);
curl_close($ch);
return new Response($code, $duration, $headers, $body, null);
}
private static function parseHeaders($raw)
{
$headers = array();
$headerLines = explode("\r\n", $raw);
foreach ($headerLines as $line) {
$headerLine = trim($line);
$kv = explode(':', $headerLine);
if (count($kv) > 1) {
$kv[0] =self::ucwordsHyphen($kv[0]);
$headers[$kv[0]] = trim($kv[1]);
}
}
return $headers;
}
private static function escapeQuotes($str)
{
$find = array("\\", "\"");
$replace = array("\\\\", "\\\"");
return str_replace($find, $replace, $str);
}
private static function ucwordsHyphen($str)
{
return str_replace('- ', '-', ucwords(str_replace('-', '- ', $str)));
}
}
... ...
<?php
namespace Qiniu\Http;
/**
* 七牛业务请求逻辑错误封装类,主要用来解析API请求返回如下的内容:
* <pre>
* {"error" : "detailed error message"}
* </pre>
*/
final class Error
{
private $url;
private $response;
public function __construct($url, $response)
{
$this->url = $url;
$this->response = $response;
}
public function code()
{
return $this->response->statusCode;
}
public function getResponse()
{
return $this->response;
}
public function message()
{
return $this->response->error;
}
}
... ...
<?php
namespace Qiniu\Http;
final class Request
{
public $url;
public $headers;
public $body;
public $method;
public function __construct($method, $url, array $headers = array(), $body = null)
{
$this->method = strtoupper($method);
$this->url = $url;
$this->headers = $headers;
$this->body = $body;
}
}
... ...
<?php
namespace Qiniu\Http;
/**
* HTTP response Object
*/
final class Response
{
public $statusCode;
public $headers;
public $body;
public $error;
private $jsonData;
public $duration;
/** @var array Mapping of status codes to reason phrases */
private static $statusTexts = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
208 => 'Already Reported',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Reserved for WebDAV advanced collections expired proposal',
426 => 'Upgrade required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates (Experimental)',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
510 => 'Not Extended',
511 => 'Network Authentication Required',
);
/**
* @param int $code 状态码
* @param double $duration 请求时长
* @param array $headers 响应头部
* @param string $body 响应内容
* @param string $error 错误描述
*/
public function __construct($code, $duration, array $headers = array(), $body = null, $error = null)
{
$this->statusCode = $code;
$this->duration = $duration;
$this->headers = $headers;
$this->body = $body;
$this->error = $error;
$this->jsonData = null;
if ($error !== null) {
return;
}
if ($body === null) {
if ($code >= 400) {
$this->error = self::$statusTexts[$code];
}
return;
}
if (self::isJson($headers)) {
try {
$jsonData = self::bodyJson($body);
if ($code >= 400) {
$this->error = $body;
if ($jsonData['error'] !== null) {
$this->error = $jsonData['error'];
}
}
$this->jsonData = $jsonData;
} catch (\InvalidArgumentException $e) {
$this->error = $body;
if ($code >= 200 && $code < 300) {
$this->error = $e->getMessage();
}
}
} elseif ($code >= 400) {
$this->error = $body;
}
return;
}
public function json()
{
return $this->jsonData;
}
private static function bodyJson($body)
{
return \Qiniu\json_decode((string) $body, true, 512);
}
public function xVia()
{
$via = $this->headers['X-Via'];
if ($via === null) {
$via = $this->headers['X-Px'];
}
if ($via === null) {
$via = $this->headers['Fw-Via'];
}
return $via;
}
public function xLog()
{
return $this->headers['X-Log'];
}
public function xReqId()
{
return $this->headers['X-Reqid'];
}
public function ok()
{
return $this->statusCode >= 200 && $this->statusCode < 300 && $this->error === null;
}
public function needRetry()
{
$code = $this->statusCode;
if ($code < 0 || ($code / 100 === 5 and $code !== 579) || $code === 996) {
return true;
}
}
private static function isJson($headers)
{
return array_key_exists('Content-Type', $headers) &&
strpos($headers['Content-Type'], 'application/json') === 0;
}
}
... ...
<?php
namespace Qiniu\Processing;
use Qiniu;
/**
* 主要涉及图片链接拼接
*
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
*/
final class ImageUrlBuilder
{
/**
* mode合法范围值
*
* @var array
*/
protected $modeArr = array(0, 1, 2, 3, 4, 5);
/**
* format合法值
*
* @var array
*/
protected $formatArr = array('psd', 'jpeg', 'png', 'gif', 'webp', 'tiff', 'bmp');
/**
* 水印图片位置合法值
*
* @var array
*/
protected $gravityArr = array('NorthWest', 'North', 'NorthEast',
'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast');
/**
* 缩略图链接拼接
*
* @param string $url 图片链接
* @param int $mode 缩略模式
* @param int $width 宽度
* @param int $height 长度
* @param string $format 输出类型
* @param int $quality 图片质量
* @param int $interlace 是否支持渐进显示
* @param int $ignoreError 忽略结果
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function thumbnail(
$url,
$mode,
$width,
$height,
$format = null,
$interlace = null,
$quality = null,
$ignoreError = 1
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
// 参数合法性效验
if (!in_array(intval($mode), $this->modeArr, true)) {
return $url;
}
if (!$width || !$height) {
return $url;
}
$thumbStr = 'imageView2/' . $mode . '/w/' . $width . '/h/' . $height . '/';
// 拼接输出格式
if (!is_null($format)
&& in_array($format, $this->formatArr)
) {
$thumbStr .= 'format/' . $format . '/';
}
// 拼接渐进显示
if (!is_null($interlace)
&& in_array(intval($interlace), array(0, 1), true)
) {
$thumbStr .= 'interlace/' . $interlace . '/';
}
// 拼接图片质量
if (!is_null($quality)
&& intval($quality) >= 0
&& intval($quality) <= 100
) {
$thumbStr .= 'q/' . $quality . '/';
}
$thumbStr .= 'ignore-error/' . $ignoreError . '/';
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $thumbStr;
}
/**
* 图片水印
*
* @param string $url 图片链接
* @param string $image 水印图片链接
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @param numeric $watermarkScale 自适应原图的短边比例
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function waterImg(
$url,
$image,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null,
$watermarkScale = null
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
$waterStr = 'watermark/1/image/' . \Qiniu\base64_urlSafeEncode($image) . '/';
// 拼接水印透明度
if (is_numeric($dissolve)
&& $dissolve <= 100
) {
$waterStr .= 'dissolve/' . $dissolve . '/';
}
// 拼接水印位置
if (in_array($gravity, $this->gravityArr, true)) {
$waterStr .= 'gravity/' . $gravity . '/';
}
// 拼接横轴边距
if (!is_null($dx)
&& is_numeric($dx)
) {
$waterStr .= 'dx/' . $dx . '/';
}
// 拼接纵轴边距
if (!is_null($dy)
&& is_numeric($dy)
) {
$waterStr .= 'dy/' . $dy . '/';
}
// 拼接自适应原图的短边比例
if (!is_null($watermarkScale)
&& is_numeric($watermarkScale)
&& $watermarkScale > 0
&& $watermarkScale < 1
) {
$waterStr .= 'ws/' . $watermarkScale . '/';
}
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
}
/**
* 文字水印
*
* @param string $url 图片链接
* @param string $text 文字
* @param string $font 文字字体
* @param string $fontSize 文字字号
* @param string $fontColor 文字颜色
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
public function waterText(
$url,
$text,
$font = '黑体',
$fontSize = 0,
$fontColor = null,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null
) {
// url合法效验
if (!$this->isUrl($url)) {
return $url;
}
$waterStr = 'watermark/2/text/'
. \Qiniu\base64_urlSafeEncode($text) . '/font/'
. \Qiniu\base64_urlSafeEncode($font) . '/';
// 拼接文字大小
if (is_int($fontSize)) {
$waterStr .= 'fontsize/' . $fontSize . '/';
}
// 拼接文字颜色
if (!is_null($fontColor)
&& $fontColor
) {
$waterStr .= 'fill/' . \Qiniu\base64_urlSafeEncode($fontColor) . '/';
}
// 拼接水印透明度
if (is_numeric($dissolve)
&& $dissolve <= 100
) {
$waterStr .= 'dissolve/' . $dissolve . '/';
}
// 拼接水印位置
if (in_array($gravity, $this->gravityArr, true)) {
$waterStr .= 'gravity/' . $gravity . '/';
}
// 拼接横轴边距
if (!is_null($dx)
&& is_numeric($dx)
) {
$waterStr .= 'dx/' . $dx . '/';
}
// 拼接纵轴边距
if (!is_null($dy)
&& is_numeric($dy)
) {
$waterStr .= 'dy/' . $dy . '/';
}
// 如果有query_string用|线分割实现多参数
return $url . ($this->hasQuery($url) ? '|' : '?') . $waterStr;
}
/**
* 效验url合法性
*
* @param string $url url链接
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
protected function isUrl($url)
{
$urlArr = parse_url($url);
return $urlArr['scheme']
&& in_array($urlArr['scheme'], array('http', 'https'))
&& $urlArr['host']
&& $urlArr['path'];
}
/**
* 检测是否有query
*
* @param string $url url链接
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
protected function hasQuery($url)
{
$urlArr = parse_url($url);
return !empty($urlArr['query']);
}
}
... ...
<?php
namespace Qiniu\Processing;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
final class Operation
{
private $auth;
private $token_expire;
private $domain;
public function __construct($domain, $auth = null, $token_expire = 3600)
{
$this->auth = $auth;
$this->domain = $domain;
$this->token_expire = $token_expire;
}
/**
* 对资源文件进行处理
*
* @param $key 待处理的资源文件名
* @param $fops string|array fop操作,多次fop操作以array的形式传入。
* eg. imageView2/1/w/200/h/200, imageMogr2/thumbnail/!75px
*
* @return array 文件处理后的结果及错误。
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/
*/
public function execute($key, $fops)
{
$url = $this->buildUrl($key, $fops);
$resp = Client::get($url);
if (!$resp->ok()) {
return array(null, new Error($url, $resp));
}
if ($resp->json() !== null) {
return array($resp->json(), null);
}
return array($resp->body, null);
}
public function buildUrl($key, $fops, $protocol = 'http')
{
if (is_array($fops)) {
$fops = implode('|', $fops);
}
$url = $protocol . "://$this->domain/$key?$fops";
if ($this->auth !== null) {
$url = $this->auth->privateDownloadUrl($url, $this->token_expire);
}
return $url;
}
}
... ...
<?php
namespace Qiniu\Processing;
use Qiniu\Config;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Processing\Operation;
/**
* 持久化处理类,该类用于主动触发异步持久化操作.
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/pfop/pfop.html
*/
final class PersistentFop
{
/**
* @var 账号管理密钥对,Auth对象
*/
private $auth;
/*
* @var 配置对象,Config 对象
* */
private $config;
public function __construct($auth, $config = null)
{
$this->auth = $auth;
if ($config == null) {
$this->config = new Config();
} else {
$this->config = $config;
}
}
/**
* 对资源文件进行异步持久化处理
* @param $bucket 资源所在空间
* @param $key 待处理的源文件
* @param $fops string|array 待处理的pfop操作,多个pfop操作以array的形式传入。
* eg. avthumb/mp3/ab/192k, vframe/jpg/offset/7/w/480/h/360
* @param $pipeline 资源处理队列
* @param $notify_url 处理结果通知地址
* @param $force 是否强制执行一次新的指令
*
*
* @return array 返回持久化处理的persistentId, 和返回的错误。
*
* @link http://developer.qiniu.com/docs/v6/api/reference/fop/
*/
public function execute($bucket, $key, $fops, $pipeline = null, $notify_url = null, $force = false)
{
if (is_array($fops)) {
$fops = implode(';', $fops);
}
$params = array('bucket' => $bucket, 'key' => $key, 'fops' => $fops);
\Qiniu\setWithoutEmpty($params, 'pipeline', $pipeline);
\Qiniu\setWithoutEmpty($params, 'notifyURL', $notify_url);
if ($force) {
$params['force'] = 1;
}
$data = http_build_query($params);
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
$url = $scheme . Config::API_HOST . '/pfop/';
$headers = $this->auth->authorization($url, $data, 'application/x-www-form-urlencoded');
$headers['Content-Type'] = 'application/x-www-form-urlencoded';
$response = Client::post($url, $data, $headers);
if (!$response->ok()) {
return array(null, new Error($url, $response));
}
$r = $response->json();
$id = $r['persistentId'];
return array($id, null);
}
public function status($id)
{
$scheme = "http://";
if ($this->config->useHTTPS === true) {
$scheme = "https://";
}
$url = $scheme . Config::API_HOST . "/status/get/prefop?id=$id";
$response = Client::get($url);
if (!$response->ok()) {
return array(null, new Error($url, $response));
}
return array($response->json(), null);
}
}
... ...
<?php
namespace Qiniu;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
class Region
{
//源站上传域名
public $srcUpHosts;
//CDN加速上传域名
public $cdnUpHosts;
//资源管理域名
public $rsHost;
//资源列举域名
public $rsfHost;
//资源处理域名
public $apiHost;
//IOVIP域名
public $iovipHost;
//构造一个Region对象
public function __construct(
$srcUpHosts = array(),
$cdnUpHosts = array(),
$rsHost = "rs.qiniu.com",
$rsfHost = "rsf.qiniu.com",
$apiHost = "api.qiniu.com",
$iovipHost = null
) {
$this->srcUpHosts = $srcUpHosts;
$this->cdnUpHosts = $cdnUpHosts;
$this->rsHost = $rsHost;
$this->rsfHost = $rsfHost;
$this->apiHost = $apiHost;
$this->iovipHost = $iovipHost;
}
//华东机房
public static function regionHuadong()
{
$regionHuadong = new Region(
array("up.qiniup.com", 'up-jjh.qiniup.com', 'up-xs.qiniup.com'),
array('upload.qiniup.com', 'upload-jjh.qiniup.com', 'upload-xs.qiniup.com'),
'rs.qbox.me',
'rsf.qbox.me',
'api.qiniu.com',
'iovip.qbox.me'
);
return $regionHuadong;
}
//华东机房内网上传
public static function qvmRegionHuadong()
{
$qvmRegionHuadong = new Region(
array("free-qvm-z0-xs.qiniup.com"),
'rs.qbox.me',
'rsf.qbox.me',
'api.qiniu.com',
'iovip.qbox.me'
);
return $qvmRegionHuadong;
}
//华北机房内网上传
public static function qvmRegionHuabei()
{
$qvmRegionHuabei = new Region(
array("free-qvm-z1-zz.qiniup.com"),
"rs-z1.qbox.me",
"rsf-z1.qbox.me",
"api-z1.qiniu.com",
"iovip-z1.qbox.me"
);
return $qvmRegionHuabei;
}
//华北机房
public static function regionHuabei()
{
$regionHuabei = new Region(
array('up-z1.qiniup.com'),
array('upload-z1.qiniup.com'),
"rs-z1.qbox.me",
"rsf-z1.qbox.me",
"api-z1.qiniu.com",
"iovip-z1.qbox.me"
);
return $regionHuabei;
}
//华南机房
public static function regionHuanan()
{
$regionHuanan = new Region(
array('up-z2.qiniup.com', 'up-dg.qiniup.com', 'up-fs.qiniup.com'),
array('upload-z2.qiniup.com', 'upload-dg.qiniup.com', 'upload-fs.qiniup.com'),
"rs-z2.qbox.me",
"rsf-z2.qbox.me",
"api-z2.qiniu.com",
"iovip-z2.qbox.me"
);
return $regionHuanan;
}
//北美机房
public static function regionNorthAmerica()
{
//北美机房
$regionNorthAmerica = new Region(
array('up-na0.qiniup.com'),
array('upload-na0.qiniup.com'),
"rs-na0.qbox.me",
"rsf-na0.qbox.me",
"api-na0.qiniu.com",
"iovip-na0.qbox.me"
);
return $regionNorthAmerica;
}
//新加坡机房
public static function regionSingapore()
{
//新加坡机房
$regionSingapore = new Region(
array('up-as0.qiniup.com'),
array('upload-as0.qiniup.com'),
"rs-as0.qbox.me",
"rsf-as0.qbox.me",
"api-as0.qiniu.com",
"iovip-as0.qbox.me"
);
return $regionSingapore;
}
/*
* GET /v2/query?ak=<ak>&&bucket=<bucket>
**/
public static function queryRegion($ak, $bucket)
{
$Region = new Region();
$url = Config::API_HOST . '/v2/query' . "?ak=$ak&bucket=$bucket";
$ret = Client::Get($url);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
//parse Region;
$iovipHost = $r['io']['src']['main'][0];
$Region->iovipHost = $iovipHost;
$accMain = $r['up']['acc']['main'][0];
array_push($Region->cdnUpHosts, $accMain);
if (isset($r['up']['acc']['backup'])) {
foreach ($r['up']['acc']['backup'] as $key => $value) {
array_push($Region->cdnUpHosts, $value);
}
}
$srcMain = $r['up']['src']['main'][0];
array_push($Region->srcUpHosts, $srcMain);
if (isset($r['up']['src']['backup'])) {
foreach ($r['up']['src']['backup'] as $key => $value) {
array_push($Region->srcUpHosts, $value);
}
}
//set specific hosts
if (strstr($Region->iovipHost, "z1") !== false) {
$Region->rsHost = "rs-z1.qbox.me";
$Region->rsfHost = "rsf-z1.qbox.me";
$Region->apiHost = "api-z1.qiniu.com";
} elseif (strstr($Region->iovipHost, "z2") !== false) {
$Region->rsHost = "rs-z2.qbox.me";
$Region->rsfHost = "rsf-z2.qbox.me";
$Region->apiHost = "api-z2.qiniu.com";
} elseif (strstr($Region->iovipHost, "na0") !== false) {
$Region->rsHost = "rs-na0.qbox.me";
$Region->rsfHost = "rsf-na0.qbox.me";
$Region->apiHost = "api-na0.qiniu.com";
} elseif (strstr($Region->iovipHost, "as0") !== false) {
$Region->rsHost = "rs-as0.qbox.me";
$Region->rsfHost = "rsf-as0.qbox.me";
$Region->apiHost = "api-as0.qiniu.com";
} else {
$Region->rsHost = "rs.qbox.me";
$Region->rsfHost = "rsf.qbox.me";
$Region->apiHost = "api.qiniu.com";
}
return $Region;
}
}
... ...
<?php
namespace Qiniu\Rtc;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Config;
use Qiniu\Auth;
class AppClient
{
private $auth;
private $baseURL;
public function __construct(Auth $auth)
{
$this->auth = $auth;
$this->baseURL = sprintf("%s/%s/apps", Config::RTCAPI_HOST, Config::RTCAPI_VERSION);
}
/*
* 创建应用
* hub: 直播空间名
* title: app 的名称 注意,Title 不是唯一标识,重复 create 动作将生成多个 app
* maxUsers:人数限制
* NoAutoKickUser: bool 类型,可选,禁止自动踢人(抢流)。默认为 false ,
即同一个身份的 client (app/room/user) ,新的连麦请求可以成功,旧连接被关闭。
*/
public function createApp($hub, $title, $maxUsers = null, $noAutoKickUser = null)
{
$params['hub'] = $hub;
$params['title'] = $title;
if (!empty($maxUsers)) {
$params['maxUsers'] = $maxUsers;
}
if ($noAutoKickUser !== null) {
$params['noAutoKickUser'] = $noAutoKickUser;
}
$body = json_encode($params);
$ret = $this->post($this->baseURL, $body);
return $ret;
}
/*
* 更新应用
* appId: app 的唯一标识,创建的时候由系统生成。
* Title: app 的名称, 可选。
* Hub: 绑定的直播 hub,可选,用于合流后 rtmp 推流。
* MaxUsers: int 类型,可选,连麦房间支持的最大在线人数。
* NoAutoKickUser: bool 类型,可选,禁止自动踢人。
* MergePublishRtmp: 连麦合流转推 RTMP 的配置,可选择。其详细配置包括如下
Enable: 布尔类型,用于开启和关闭所有房间的合流功能。
AudioOnly: 布尔类型,可选,指定是否只合成音频。
Height, Width: int64,可选,指定合流输出的高和宽,默认为 640 x 480。
OutputFps: int64,可选,指定合流输出的帧率,默认为 25 fps 。
OutputKbps: int64,可选,指定合流输出的码率,默认为 1000 。
URL: 合流后转推旁路直播的地址,可选,支持魔法变量配置按照连麦房间号生成不同的推流地址。如果是转推到七牛直播云,不建议使用该配置。
StreamTitle: 转推七牛直播云的流名,可选,支持魔法变量配置按照连麦房间号生成不同的流名。例如,配置 Hub 为 qn-zhibo ,配置 StreamTitle 为 $(roomName) ,则房间 meeting-001 的合流将会被转推到 rtmp://pili-publish.qn-zhibo.***.com/qn-zhibo/meeting-001地址。详细配置细则,请咨询七牛技术支持。
*/
public function updateApp($appId, $hub, $title, $maxUsers = null, $mergePublishRtmp = null, $noAutoKickUser = null)
{
$url = $this->baseURL . '/' . $appId;
$params['hub'] = $hub;
$params['title'] = $title;
if (!empty($maxUsers)) {
$params['maxUsers'] = $maxUsers;
}
if ($noAutoKickUser !== null) {
$params['noAutoKickUser'] = $noAutoKickUser;
}
if (!empty($mergePublishRtmp)) {
$params['mergePublishRtmp'] = $mergePublishRtmp;
}
$body = json_encode($params);
$ret = $this->post($url, $body);
return $ret;
}
/*
* 获取应用信息
* appId: app 的唯一标识,创建的时候由系统生成。
*/
public function getApp($appId)
{
$url = $this->baseURL . '/' . $appId;
$ret = $this->get($url);
return $ret;
}
/*
* 删除应用
* appId: app 的唯一标识,创建的时候由系统生成
*/
public function deleteApp($appId)
{
$url = $this->baseURL . '/' . $appId;
list(, $err) = $this->delete($url);
return $err;
}
/*
* 获取房间内用户列表
* appId: app 的唯一标识,创建的时候由系统生成。
* roomName: 操作所查询的连麦房间。
*/
public function listUser($appId, $roomName)
{
$url = sprintf("%s/%s/rooms/%s/users", $this->baseURL, $appId, $roomName);
$ret = $this->get($url);
return $ret;
}
/*
* 踢出用户
* appId: app 的唯一标识,创建的时候由系统生成。
* roomName: 连麦房间
* userId: 请求加入房间的用户ID
*/
public function kickUser($appId, $roomName, $userId)
{
$url = sprintf("%s/%s/rooms/%s/users/%s", $this->baseURL, $appId, $roomName, $userId);
list(, $err) = $this->delete($url);
return $err;
}
/*
* 获取应用中活跃房间
* appId: app 的唯一标识,创建的时候由系统生成。
* prefix: 所查询房间名的前缀索引,可以为空。
* offset: int 类型,分页查询的位移标记。
* limit: int 类型,此次查询的最大长度。
* GET /v3/apps/<AppID>/rooms?prefix=<RoomNamePrefix>&offset=<Offset>&limit=<Limit>
*/
public function listActiveRooms($appId, $prefix = null, $offset = null, $limit = null)
{
if (isset($prefix)) {
$query['prefix'] = $prefix;
}
if (isset($offset)) {
$query['offset'] = $offset;
}
if (isset($limit)) {
$query['limit'] = $limit;
}
if (isset($query) && !empty($query)) {
$query = '?' . http_build_query($query);
$url = sprintf("%s/%s/rooms%s", $this->baseURL, $appId, $query);
} else {
$url = sprintf("%s/%s/rooms", $this->baseURL, $appId);
}
$ret = $this->get($url);
return $ret;
}
/*
* 生成加入房间的令牌
* appId: app 的唯一标识,创建的时候由系统生成。
* roomName: 房间名称,需满足规格 ^[a-zA-Z0-9_-]{3,64}$
* userId: 请求加入房间的用户 ID,需满足规格 ^[a-zA-Z0-9_-]{3,50}$
* expireAt: int64 类型,鉴权的有效时间,传入以秒为单位的64位Unix
绝对时间,token 将在该时间后失效。
* permission: 该用户的房间管理权限,"admin" 或 "user",默认为 "user" 。
当权限角色为 "admin" 时,拥有将其他用户移除出房间等特权.
*/
public function appToken($appId, $roomName, $userId, $expireAt, $permission)
{
$params['appId'] = $appId;
$params['userId'] = $userId;
$params['roomName'] = $roomName;
$params['permission'] = $permission;
$params['expireAt'] = $expireAt;
$appAccessString = json_encode($params);
return $this->auth->signWithData($appAccessString);
}
private function get($url, $cType = null)
{
$rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
$rtcToken['Content-Type'] = $cType;
$ret = Client::get($url, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function delete($url, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::delete($url, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function post($url, $body, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::post($url, $body, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
}
... ...
<?php
namespace Qiniu\Sms;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
use Qiniu\Config;
use Qiniu\Auth;
class Sms
{
private $auth;
private $baseURL;
public function __construct(Auth $auth)
{
$this->auth = $auth;
$this->baseURL = sprintf("%s/%s/", Config::SMS_HOST, Config::SMS_VERSION);
}
/*
* 创建签名
* signature: string 类型,必填,【长度限制8个字符内】超过长度会报错
* source: string 类型,必填,申请签名时必须指定签名来源。取值范围为:
nterprises_and_institutions 企事业单位的全称或简称
website 工信部备案网站的全称或简称
app APP应用的全称或简称
public_number_or_small_program 公众号或小程序的全称或简称
store_name 电商平台店铺名的全称或简称
trade_name 商标名的全称或简称,
* pics: 本地的图片路径 string 类型,可选
*@return: 类型array {
"signature_id": <signature_id>
}
*/
public function createSignature($signature, $source, $pics = null)
{
$params['signature'] = $signature;
$params['source'] = $source;
if (!empty($pics)) {
$params['pics'] = $this->imgToBase64($pics);
}
$body = json_encode($params);
$url =$this->baseURL.'signature';
$ret = $this->post($url, $body);
return $ret;
}
/*
* 编辑签名
* id 签名id : string 类型,必填,
* signature: string 类型,必填,
* source: string 类型,必填,申请签名时必须指定签名来源。取值范围为:
enterprises_and_institutions 企事业单位的全称或简称
website 工信部备案网站的全称或简称
app APP应用的全称或简称
public_number_or_small_program 公众号或小程序的全称或简称
store_name 电商平台店铺名的全称或简称
trade_name 商标名的全称或简称,
* pics: 本地的图片路径 string 类型,可选,
* @return: 类型array {
"signature": string
}
*/
public function updateSignature($id, $signature, $source, $pics = null)
{
$params['signature'] = $signature;
$params['source'] = $source;
if (!empty($pics)) {
$params['pics'] = $this->imgToBase64($pics);
}
$body = json_encode($params);
$url =$this->baseURL.'signature/'.$id;
$ret = $this->PUT($url, $body);
return $ret;
}
/*
* 查询签名
* audit_status: 审核状态 string 类型,可选,
取值范围为: "passed"(通过), "rejected"(未通过), "reviewing"(审核中)
* page:页码 int 类型,
* page_size: 分页大小 int 类型,可选, 默认为20
*@return: 类型array {
"items": [{
"id": string,
"signature": string,
"source": string,
"audit_status": string,
"reject_reason": string,
"created_at": int64,
"updated_at": int64
}...],
"total": int,
"page": int,
"page_size": int,
}
*/
public function checkSignature($audit_status = null, $page = 1, $page_size = 20)
{
$url = sprintf(
"%s?audit_status=%s&page=%s&page_size=%s",
$this->baseURL.'signature',
$audit_status,
$page,
$page_size
);
$ret = $this->get($url);
return $ret;
}
/*
* 删除签名
* id 签名id string 类型,必填,
* @retrun : 请求成功 HTTP 状态码为 200
*/
public function deleteSignature($id)
{
$url = $this->baseURL . 'signature/' . $id;
list(, $err) = $this->delete($url);
return $err;
}
/*
* 创建模板
* name : 模板名称 string 类型 ,必填
* template: 模板内容 string 类型,必填
* type: 模板类型 string 类型,必填,
取值范围为: notification (通知类短信), verification (验证码短信), marketing (营销类短信)
* description: 申请理由简述 string 类型,必填
* signature_id: 已经审核通过的签名 string 类型,必填
* @return: 类型 array {
"template_id": string
}
*/
public function createTemplate(
$name,
$template,
$type,
$description,
$signture_id
) {
$params['name'] = $name;
$params['template'] = $template;
$params['type'] = $type;
$params['description'] = $description;
$params['signature_id'] = $signture_id;
$body = json_encode($params);
$url =$this->baseURL.'template';
$ret = $this->post($url, $body);
return $ret;
}
/*
* 查询模板
* audit_status: 审核状态 string 类型 ,可选,
取值范围为: passed (通过), rejected (未通过), reviewing (审核中)
* page: 页码 int 类型,可选,默认为 1
* page_size: 分页大小 int 类型,可选,默认为 20
* @return: 类型array{
"items": [{
"id": string,
"name": string,
"template": string,
"audit_status": string,
"reject_reason": string,
"type": string,
"signature_id": string, // 模版绑定的签名ID
"signature_text": string, // 模版绑定的签名内容
"created_at": int64,
"updated_at": int64
}...],
"total": int,
"page": int,
"page_size": int
}
*/
public function queryTemplate($audit_status = null, $page = 1, $page_size = 20)
{
$url = sprintf(
"%s?audit_status=%s&page=%s&page_size=%s",
$this->baseURL.'template',
$audit_status,
$page,
$page_size
);
$ret = $this->get($url);
return $ret;
}
/*
* 编辑模板
* id :模板id
* name : 模板名称 string 类型 ,必填
* template: 模板内容 string 类型,必填
* description: 申请理由简述 string 类型,必填
* signature_id: 已经审核通过的签名 string 类型,必填
* @retrun : 请求成功 HTTP 状态码为 200
*/
public function updateTemplate(
$id,
$name,
$template,
$description,
$signature_id
) {
$params['name'] = $name;
$params['template'] = $template;
$params['description'] = $description;
$params['signature_id'] = $signature_id;
$body = json_encode($params);
$url =$this->baseURL.'template/'.$id;
$ret = $this->PUT($url, $body);
return $ret;
}
/*
* 删除模板
* id :模板id string 类型,必填,
* @retrun : 请求成功 HTTP 状态码为 200
*/
public function deleteTemplate($id)
{
$url = $this->baseURL . 'template/' . $id;
list(, $err) = $this->delete($url);
return $err;
}
/*
* 发送短信
* 编辑模板
* template_id :模板id string类型,必填
* mobiles : 手机号数组 []string 类型 ,必填
* parameters: 模板内容 map[string]string 类型,可选
* @return: 类型json {
"job_id": string
}
*/
public function sendMessage($template_id, $mobiles, $parameters = null)
{
$params['template_id'] = $template_id;
$params['mobiles'] = $mobiles;
if (!empty($parameters)) {
$params['parameters'] = $parameters;
}
$body = json_encode($params);
$url =$this->baseURL.'message';
$ret = $this->post($url, $body);
return $ret;
}
public function imgToBase64($img_file)
{
$img_base64 = '';
if (file_exists($img_file)) {
$app_img_file = $img_file; // 图片路径
$img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
$fp = fopen($app_img_file, "r"); // 图片是否可读权限
if ($fp) {
$filesize = filesize($app_img_file);
if ($filesize > 5*1024*1024) {
die("pic size < 5M !");
}
$content = fread($fp, $filesize);
$file_content = chunk_split(base64_encode($content)); // base64编码
switch ($img_info[2]) { //判读图片类型
case 1:
$img_type = 'gif';
break;
case 2:
$img_type = 'jpg';
break;
case 3:
$img_type = 'png';
break;
}
//合成图片的base64编码
$img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;
}
fclose($fp);
}
return $img_base64;
}
private function get($url, $cType = null)
{
$rtcToken = $this->auth->authorizationV2($url, "GET", null, $cType);
$rtcToken['Content-Type'] = $cType;
$ret = Client::get($url, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function delete($url, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "DELETE", null, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::delete($url, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function post($url, $body, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "POST", $body, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::post($url, $body, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
private function PUT($url, $body, $contentType = 'application/json')
{
$rtcToken = $this->auth->authorizationV2($url, "PUT", $body, $contentType);
$rtcToken['Content-Type'] = $contentType;
$ret = Client::put($url, $body, $rtcToken);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
}
... ...
<?php
namespace Qiniu\Storage;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Zone;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
/**
* 主要涉及了鉴黄接口的实现,具体的接口规格可以参考
*
* @link https://developer.qiniu.com/dora/manual/3674/kodo-product-introduction
*/
final class ArgusManager
{
private $auth;
private $config;
public function __construct(Auth $auth, Config $config = null)
{
$this->auth = $auth;
if ($config == null) {
$this->config = new Config();
} else {
$this->config = $config;
}
}
/**
* 视频鉴黄
*
* @param $body body信息
* @param $vid videoID
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link https://developer.qiniu.com/dora/manual/4258/video-pulp
*/
public function pulpVideo($body, $vid)
{
$path = '/v1/video/' . $vid;
return $this->arPost($path, $body);
}
private function getArHost()
{
$scheme = "http://";
if ($this->config->useHTTPS == true) {
$scheme = "https://";
}
return $scheme . Config::ARGUS_HOST;
}
private function arPost($path, $body = null)
{
$url = $this->getArHost() . $path;
return $this->post($url, $body);
}
private function post($url, $body)
{
$headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
$headers['Content-Type']='application/json';
$ret = Client::post($url, $body, $headers);
if (!$ret->ok()) {
print($ret->statusCode);
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
}
... ...
<?php
namespace Qiniu\Storage;
use Qiniu\Auth;
use Qiniu\Config;
use Qiniu\Zone;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
/**
* 主要涉及了空间资源管理及批量操作接口的实现,具体的接口规格可以参考
*
* @link https://developer.qiniu.com/kodo/api/1274/rs
*/
final class BucketManager
{
private $auth;
private $config;
public function __construct(Auth $auth, Config $config = null)
{
$this->auth = $auth;
if ($config == null) {
$this->config = new Config();
} else {
$this->config = $config;
}
}
/**
* 获取指定账号下所有的空间名。
*
* @return string[] 包含所有空间名
*/
public function buckets($shared = true)
{
$includeShared = "false";
if ($shared === true) {
$includeShared = "true";
}
return $this->rsGet('/buckets?shared=' . $includeShared);
}
/**
* 列举空间,返回bucket列表
* region 指定区域,global 指定全局空间。
* 在指定了 region 参数时,
* 如果指定 global 为 true,那么忽略 region 参数指定的区域,返回所有区域的全局空间。
* 如果没有指定 global 为 true,那么返回指定区域中非全局空间。
* 在没有指定 region 参数时(包括指定为空""),
* 如果指定 global 为 true,那么返回所有区域的全局空间。
* 如果没有指定 global 为 true,那么返回指定区域中所有的空间,包括全局空间。
* 在指定了line为 true 时,只返回 Line 空间;否则,只返回非 Line 空间。
* share 参数用于指定共享空间。
*/
public function listbuckets(
$region = null,
$line = 'false',
$shared = 'false'
) {
$path = '/v3/buckets?region=' . $region . '&line=' . $line . '&shared=' . $shared;
$info = $this->ucPost($path);
return $info;
}
/**
* 创建空间
*
* @param $name 创建的空间名
* @param $region 创建的区域,默认华东
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function createBucket($name, $region = 'z0')
{
$path = '/mkbucketv2/'.$name.'/region/' . $region;
return $this->rsPost($path, null);
}
/**
* 删除空间
*
* @param $name 删除的空间名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function deleteBucket($name)
{
$path = '/drop/'.$name;
return $this->rsPost($path, null);
}
/**
* 获取指定空间绑定的所有的域名
*
* @return string[] 包含所有空间域名
*/
public function domains($bucket)
{
return $this->apiGet('/v6/domain/list?tbl=' . $bucket);
}
/**
* 获取指定空间的相关信息
*
* @return string[] 包含空间信息
*/
public function bucketInfo($bucket)
{
$path = '/v2/bucketInfo?bucket=' . $bucket;
$info = $this->ucPost($path);
return $info;
}
/**
* 获取指定zone的空间信息列表
* 在Region 未指定且Global 不为 true 时(包含未指定的情况,下同),返回用户的所有空间。
* 在指定了 region 参数且 global 不为 true 时,只列举非全局空间。
* shared 不指定shared参数或指定shared为rw或false时,返回包含具有读写权限空间,
* 指定shared为rd或true时,返回包含具有读权限空间。
* fs:如果为 true,会返回每个空间当前的文件数和存储量(实时数据)。
* @return string[] 包含空间信息
*/
public function bucketInfos($region = null, $shared = 'false', $fs = 'false')
{
$path = '/v2/bucketInfos?region=' . $region . '&shared=' . $shared . '&fs=' . $fs;
$info = $this->ucPost($path);
return $info;
}
/**
* 获取空间绑定的域名列表
* @return string[] 包含空间绑定的所有域名
*/
/**
* 列取空间的文件列表
*
* @param $bucket 空间名
* @param $prefix 列举前缀
* @param $marker 列举标识符
* @param $limit 单次列举个数限制
* @param $delimiter 指定目录分隔符
*
* @return array 包含文件信息的数组,类似:[
* {
* "hash" => "<Hash string>",
* "key" => "<Key string>",
* "fsize" => "<file size>",
* "putTime" => "<file modify time>"
* },
* ...
* ]
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
*/
public function listFiles(
$bucket,
$prefix = null,
$marker = null,
$limit = 1000,
$delimiter = null
) {
$query = array('bucket' => $bucket);
\Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
\Qiniu\setWithoutEmpty($query, 'marker', $marker);
\Qiniu\setWithoutEmpty($query, 'limit', $limit);
\Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
$url = $this->getRsfHost() . '/list?' . http_build_query($query);
return $this->get($url);
}
/**
* 列取空间的文件列表
*
* @param $bucket 空间名
* @param $prefix 列举前缀
* @param $marker 列举标识符
* @param $limit 单次列举个数限制
* @param $delimiter 指定目录分隔符
* @param $skipconfirm 是否跳过已删除条目的确认机制
*
* @return array 包含文件信息的数组,类似:[
* {
* "hash" => "<Hash string>",
* "key" => "<Key string>",
* "fsize" => "<file size>",
* "putTime" => "<file modify time>"
* },
* ...
* ]
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/list.html
*/
public function listFilesv2(
$bucket,
$prefix = null,
$marker = null,
$limit = 1000,
$delimiter = null,
$skipconfirm = true
) {
$query = array('bucket' => $bucket);
\Qiniu\setWithoutEmpty($query, 'prefix', $prefix);
\Qiniu\setWithoutEmpty($query, 'marker', $marker);
\Qiniu\setWithoutEmpty($query, 'limit', $limit);
\Qiniu\setWithoutEmpty($query, 'delimiter', $delimiter);
\Qiniu\setWithoutEmpty($query, 'skipconfirm', $skipconfirm);
$path = '/v2/list?' . http_build_query($query);
$url = $this->getRsfHost() . $path;
$headers = $this->auth->authorization($url, null, 'application/x-www-form-urlencoded');
$ret = Client::post($url, null, $headers);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = explode("\n", $ret->body);
$pop = array_pop($r);
return array($r, null);
}
/**
* 设置Referer防盗链
*
* @param $bucket 空间名
* @param $mode 0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
* 1: 表示设置Referer白名单; 2:表示设置Referer黑名单
* @param $norefer 0: 表示不允许空 Refer 访问; 1: 表示允许空 Refer 访问
* @param $pattern 规则字符串, 当前允许格式分为三种: 一种为空主机头域名,
* 比如 foo.com; 一种是泛域名,比如 *.bar.com; 一种是完全通配符,
* 即一个 *; 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
* @param $source_enabled 源站是否支持,默认为0只给CDN配置, 设置为1表示开启源站防盗链
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
// public function referAntiLeech(){
// }
/**
* 增加bucket生命规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,只能为
* 字母、数字、下划线
* @param $prefix 同一个 bucket 里面前缀不能重复
* @param $delete_after_days 指定上传文件多少天后删除,指定为0表示不删除,
* 大于0表示多少天后删除,需大于 to_line_after_days
* @param $to_line_after_days 指定文件上传多少天后转低频存储。指定为0表示
* 不转低频存储,小于0表示上传的文件立即变低频存储
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function bucketLifecycleRule(
$bucket,
$name,
$prefix,
$delete_after_days,
$to_line_after_days
) {
$path = '/rules/add';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
if ($prefix) {
$params['prefix'] = $prefix;
}
if ($delete_after_days) {
$params['delete_after_days'] = $delete_after_days;
}
if ($to_line_after_days) {
$params['to_line_after_days'] = $to_line_after_days;
}
$data = http_build_query($params);
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 更新bucket生命规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,只能为字母、
* 数字、下划线
* @param $prefix 同一个 bucket 里面前缀不能重复
* @param $delete_after_days 指定上传文件多少天后删除,指定为0表示不删除,
* 大于0表示多少天后删除,需大于 to_line_after_days
* @param $to_line_after_days 指定文件上传多少天后转低频存储。指定为0表示不
* 转低频存储,小于0表示上传的文件立即变低频存储
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function updateBucketLifecycleRule(
$bucket,
$name,
$prefix,
$delete_after_days,
$to_line_after_days
) {
$path = '/rules/update';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
if ($prefix) {
$params['prefix'] = $prefix;
}
if ($delete_after_days) {
$params['delete_after_days'] = $delete_after_days;
}
if ($to_line_after_days) {
$params['to_line_after_days'] = $to_line_after_days;
}
$data = http_build_query($params);
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 获取bucket生命规则
*
* @param $bucket 空间名
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function getBucketLifecycleRules($bucket)
{
$path = '/rules/get?bucket=' . $bucket;
$info = $this->ucGet($path);
return $info;
}
/**
* 删除bucket生命规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
* 只能为字母、数字、下划线()
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function deleteBucketLifecycleRule($bucket, $name)
{
$path = '/rules/delete';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
$data = http_build_query($params);
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 增加bucket事件通知规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
* 只能为字母、数字、下划线()
* @param $prefix 同一个 bucket 里面前缀不能重复
* @param $suffix 可选,文件配置的后缀
* @param $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,
* disable,enable,deleteMarkerCreate
* @param $callbackURL 通知URL,可以指定多个,失败依次重试
* @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
* @param $host 可选,通知请求的host
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function putBucketEvent(
$bucket,
$name,
$prefix,
$suffix,
$event,
$callbackURL,
$access_key = null,
$host = null
) {
$path = '/events/add';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
if ($prefix) {
$params['prefix'] = $prefix;
}
if ($suffix) {
$params['suffix'] = $suffix;
}
if ($callbackURL) {
$params['callbackURL'] = $callbackURL;
}
if ($access_key) {
$params['access_key'] = $access_key;
}
if ($host) {
$params['host'] = $host;
}
$data = http_build_query($params);
if ($event) {
$eventpath = "";
foreach ($event as $key => $value) {
$eventpath .= "&event=$value";
}
$data .= $eventpath;
}
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 更新bucket事件通知规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
* 只能为字母、数字、下划线()
* @param $prefix 同一个 bucket 里面前缀不能重复
* @param $suffix 可选,文件配置的后缀
* @param $event 事件类型,可以指定多个,包括 put,mkfile,delete,copy,move,append,disable,
* enable,deleteMarkerCreate
* @param $callbackURL 通知URL,可以指定多个,失败依次重试
* @param $access_key 可选,设置的话会对通知请求用对应的ak、sk进行签名
* @param $host 可选,通知请求的host
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function updateBucketEvent(
$bucket,
$name,
$prefix,
$suffix,
$event,
$callbackURL,
$access_key = null,
$host = null
) {
$path = '/events/update';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
if ($prefix) {
$params['prefix'] = $prefix;
}
if ($suffix) {
$params['suffix'] = $suffix;
}
if ($event) {
$params['event'] = $event;
}
if ($callbackURL) {
$params['callbackURL'] = $callbackURL;
}
if ($access_key) {
$params['access_key'] = $access_key;
}
if ($host) {
$params['host'] = $host;
}
$data = http_build_query($params);
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 获取bucket事件通知规则
*
* @param $bucket 空间名
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function getBucketEvents($bucket)
{
$path = '/events/get?bucket=' . $bucket;
$info = $this->ucGet($path);
return $info;
}
/**
* 删除bucket事件通知规则
*
* @param $bucket 空间名
* @param $name 规则名称 bucket 内唯一,长度小于50,不能为空,
* 只能为字母、数字、下划线
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function deleteBucketEvent($bucket, $name)
{
$path = '/events/delete';
if ($bucket) {
$params['bucket'] = $bucket;
}
if ($name) {
$params['name'] = $name;
}
$data = http_build_query($params);
$info = $this->ucPost($path, $data);
return $info;
}
/**
* 设置bucket的跨域信息,最多允许设置10条跨域规则。
* 对于同一个域名如果设置了多条规则,那么按顺序使用第一条匹配的规则去生成返回值。
* 对于简单跨域请求,只匹配 Origin;
* allowed_orgin: 允许的域名。必填;支持通配符*;*表示全部匹配;只有第一个*生效;
* 需要设置"Scheme";大小写敏感。例如
* 规则:http://*.abc.*.com 请求:"http://test.abc.test.com" 结果:不通过
* 规则:"http://abc.com" 请求:"https://abc.com"/"abc.com" 结果:不通过
* 规则:"abc.com" 请求:"http://abc.com" 结果:不通过
* allowed_method: 允许的方法。必填;不支持通配符;大小写不敏感;
* allowed_header: 允许的header。选填;支持通配符*,
* 但只能是单独的*,表示允许全部header,其他*不生效;
* 空则不允许任何header;大小写不敏感;
* exposed_header: 暴露的header。选填;不支持通配符;
* X-Log, X-Reqid是默认会暴露的两个header;
* 其他的header如果没有设置,则不会暴露;大小写不敏感;
* max_age: 结果可以缓存的时间。选填;空则不缓存;
* allowed_credentials:该配置不支持设置,默认为true。
* 备注:如果没有设置任何corsRules,那么默认允许所有的跨域请求
*/
// public function putCorsRules(string $bucket, array $params)
// {
// $path = '/corsRules/set/' . $bucket;
// $data = json_encode($params);
// $info = $this->ucPost($path, $data);
// return $info;
// }
/**
* 获取bucket的跨域信息
* $bucket 空间名
*/
public function getCorsRules($bucket)
{
$path = '/corsRules/get/' . $bucket;
$info = $this->ucGet($path);
return $info;
}
/**
* 设置回源规则
* 使用该API设置源站优先级高于/image设置的源站,即IO优先读取source接口设置的源站配置,
* 如果存在会忽略/image设置的源站
* Bucket 空间名
* Host(可选)回源Host
* RetryCodes(可选),镜像回源时源站返回Code可以重试,最多指定3个,当前只支持4xx错误码重试
* SourceQiniuAK,SourceQiniuSK(可选)如果存在将在回源时对URL进行签名,客户源站可以验证
* 以保证请求来自Qiniu服务器
* Expires(可选) 签名过期时间,如果不设置默认为1小时
* Addr 回源地址,不可重复。
* Weight 权重,范围限制1-100,不填默认为1,回源时会根据所有源的权重值进行源站选择,
* 主备源会分开计算.
* Backup 是否备用回源,回源优先尝试主源
*/
// public function putBucktSourceConfig(array $params)
// {
// $path = '/mirrorConfig/set';
// $data = json_encode($params);
// $info = $this->ucPostV2($path, $data);
// return $info;
// }
/**
* 获取空间回源配置
*/
public function getBucktSourceConfig(array $params)
{
$path = '/mirrorConfig/get';
$data = json_encode($params);
$info = $this->ucPostV2($path, $data);
return $info;
}
/**
* 开关原图保护
* mode 为1表示开启原图保护,0表示关闭
*/
public function putBucketAccessStyleMode($bucket, $mode)
{
$path = '/accessMode/' . $bucket . '/mode/' . $mode;
$info = $this->ucPost($path, null);
return $info;
}
/**
* 设置私有属性
* private为0表示公开,为1表示私有
*/
public function putBucketAccessMode($bucket, $private)
{
$path = '/bucket/' . $bucket . '/private/' . $private;
$info = $this->ucPost($path, null);
return $info;
}
/**
* 设置referer防盗链
* bucket=<BucketName>: bucket 名
* mode=<AntiLeechMode>:
* 0: 表示关闭Referer(使用此选项将会忽略以下参数并将恢复默认值);
* 1: 表示设置Referer白名单; 2: 表示设置Referer黑名单
* norefer=<NoRefer>: 0: 表示不允许空 Refer 访问;
* 1: 表示允许空 Refer 访问
* pattern=<Pattern>: 规则字符串, 当前允许格式分为三种:
* 一种为空主机头域名, 比如 foo.com;
* 一种是泛域名, 比如 *.bar.com; 一种是完全通配符, 即一个 *;
* 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
* 空主机头域名可以是多级域名,比如 foo.bar.com。
* 多个域名之间不允许夹带空白字符。
* source_enabled=:1
*/
public function putReferAntiLeech($bucket, $mode, $norefer, $pattern, $enabled = 1)
{
$path = "/referAntiLeech?bucket=$bucket&mode=$mode&norefer=$norefer&pattern=$pattern&source_enabled=$enabled";
$info = $this->ucPost($path, null);
return $info;
}
/**
* 设置Bucket的maxAge
* maxAge为0或者负数表示为默认值(31536000)
*/
public function putBucketMaxAge($bucket, $maxAge)
{
$path = '/maxAge?bucket=' . $bucket . '&maxAge=' . $maxAge;
$info = $this->ucPost($path, null);
return $info;
}
/**
* 设置配额
* <bucket>: 空间名称,不支持授权空间
* <size>: 空间存储量配额,参数传入0或不传表示不更改当前配置,传入-1表示取消限额,
* 新创建的空间默认没有限额。
* <count>: 空间文件数配额,参数含义同<size>
*/
public function putBucketQuota($bucket, $size, $count)
{
$path = '/setbucketquota/' . $bucket . '/size/' . $size . '/count/' . $count;
$info = $this->apiPost($path, null);
return $info;
}
/**
* 获取配额
* bucket 空间名称
*/
public function getBucketQuota($bucket)
{
$path = '/getbucketquota/' . $bucket;
$info = $this->apiPost($path, null);
return $info;
}
/**
* 获取资源的元信息,但不返回文件内容
*
* @param $bucket 待获取信息资源所在的空间
* @param $key 待获取资源的文件名
*
* @return array 包含文件信息的数组,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>",
* "fsize" => <file size>,
* "putTime" => "<file modify time>"
* "fileType" => <file type>
* ]
*
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/stat.html
*/
public function stat($bucket, $key)
{
$path = '/stat/' . \Qiniu\entry($bucket, $key);
return $this->rsGet($path);
}
/**
* 删除指定资源
*
* @param $bucket 待删除资源所在的空间
* @param $key 待删除资源的文件名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/delete.html
*/
public function delete($bucket, $key)
{
$path = '/delete/' . \Qiniu\entry($bucket, $key);
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 给资源进行重命名,本质为move操作。
*
* @param $bucket 待操作资源所在空间
* @param $oldname 待操作资源文件名
* @param $newname 目标资源文件名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
*/
public function rename($bucket, $oldname, $newname)
{
return $this->move($bucket, $oldname, $bucket, $newname);
}
/**
* 对资源进行复制。
*
* @param $from_bucket 待操作资源所在空间
* @param $from_key 待操作资源文件名
* @param $to_bucket 目标资源空间名
* @param $to_key 目标资源文件名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/copy.html
*/
public function copy($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
{
$from = \Qiniu\entry($from_bucket, $from_key);
$to = \Qiniu\entry($to_bucket, $to_key);
$path = '/copy/' . $from . '/' . $to;
if ($force === true) {
$path .= '/force/true';
}
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 将资源从一个空间到另一个空间
*
* @param $from_bucket 待操作资源所在空间
* @param $from_key 待操作资源文件名
* @param $to_bucket 目标资源空间名
* @param $to_key 目标资源文件名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/move.html
*/
public function move($from_bucket, $from_key, $to_bucket, $to_key, $force = false)
{
$from = \Qiniu\entry($from_bucket, $from_key);
$to = \Qiniu\entry($to_bucket, $to_key);
$path = '/move/' . $from . '/' . $to;
if ($force) {
$path .= '/force/true';
}
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 主动修改指定资源的文件元信息
*
* @param $bucket 待操作资源所在空间
* @param $key 待操作资源文件名
* @param $mime 待操作文件目标mimeType
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/chgm.html
*/
public function changeMime($bucket, $key, $mime)
{
$resource = \Qiniu\entry($bucket, $key);
$encode_mime = \Qiniu\base64_urlSafeEncode($mime);
$path = '/chgm/' . $resource . '/mime/' . $encode_mime;
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 修改指定资源的存储类型
*
* @param $bucket 待操作资源所在空间
* @param $key 待操作资源文件名
* @param $fileType 待操作文件目标文件类型
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link https://developer.qiniu.com/kodo/api/3710/modify-the-file-type
*/
public function changeType($bucket, $key, $fileType)
{
$resource = \Qiniu\entry($bucket, $key);
$path = '/chtype/' . $resource . '/type/' . $fileType;
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 修改文件的存储状态,即禁用状态和启用状态间的的互相转换
*
* @param $bucket 待操作资源所在空间
* @param $key 待操作资源文件名
* @param $status 待操作文件目标文件类型
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link https://developer.qiniu.com/kodo/api/4173/modify-the-file-status
*/
public function changeStatus($bucket, $key, $status)
{
$resource = \Qiniu\entry($bucket, $key);
$path = '/chstatus/' . $resource . '/status/' . $status;
list(, $error) = $this->rsPost($path);
return $error;
}
/**
* 从指定URL抓取资源,并将该资源存储到指定空间中
*
* @param $url 指定的URL
* @param $bucket 目标资源空间
* @param $key 目标资源文件名
*
* @return array 包含已拉取的文件信息。
* 成功时: [
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ],
* null
* ]
*
* 失败时: [
* null,
* Qiniu/Http/Error
* ]
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/fetch.html
*/
public function fetch($url, $bucket, $key = null)
{
$resource = \Qiniu\base64_urlSafeEncode($url);
$to = \Qiniu\entry($bucket, $key);
$path = '/fetch/' . $resource . '/to/' . $to;
$ak = $this->auth->getAccessKey();
$ioHost = $this->config->getIovipHost($ak, $bucket);
$url = $ioHost . $path;
return $this->post($url, null);
}
/**
* 从镜像源站抓取资源到空间中,如果空间中已经存在,则覆盖该资源
*
* @param $bucket 待获取资源所在的空间
* @param $key 代获取资源文件名
*
* @return mixed 成功返回NULL,失败返回对象Qiniu\Http\Error
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/prefetch.html
*/
public function prefetch($bucket, $key)
{
$resource = \Qiniu\entry($bucket, $key);
$path = '/prefetch/' . $resource;
$ak = $this->auth->getAccessKey();
$ioHost = $this->config->getIovipHost($ak, $bucket);
$url = $ioHost . $path;
list(, $error) = $this->post($url, null);
return $error;
}
/**
* 在单次请求中进行多个资源管理操作
*
* @param $operations 资源管理操作数组
*
* @return array 每个资源的处理情况,结果类似:
* [
* { "code" => <HttpCode int>, "data" => <Data> },
* { "code" => <HttpCode int> },
* { "code" => <HttpCode int> },
* { "code" => <HttpCode int> },
* { "code" => <HttpCode int>, "data" => { "error": "<ErrorMessage string>" } },
* ...
* ]
* @link http://developer.qiniu.com/docs/v6/api/reference/rs/batch.html
*/
public function batch($operations)
{
$params = 'op=' . implode('&op=', $operations);
return $this->rsPost('/batch', $params);
}
/**
* 设置文件的生命周期
*
* @param $bucket 设置文件生命周期文件所在的空间
* @param $key 设置文件生命周期文件的文件名
* @param $days 设置该文件多少天后删除,当$days设置为0时表示取消该文件的生命周期
*
* @return Mixed
* @link https://developer.qiniu.com/kodo/api/update-file-lifecycle
*/
public function deleteAfterDays($bucket, $key, $days)
{
$entry = \Qiniu\entry($bucket, $key);
$path = "/deleteAfterDays/$entry/$days";
list(, $error) = $this->rsPost($path);
return $error;
}
private function getRsfHost()
{
$scheme = "http://";
if ($this->config->useHTTPS == true) {
$scheme = "https://";
}
return $scheme . Config::RSF_HOST;
}
private function getRsHost()
{
$scheme = "http://";
if ($this->config->useHTTPS == true) {
$scheme = "https://";
}
return $scheme . Config::RS_HOST;
}
private function getApiHost()
{
$scheme = "http://";
if ($this->config->useHTTPS == true) {
$scheme = "https://";
}
return $scheme . Config::API_HOST;
}
private function getUcHost()
{
$scheme = "http://";
if ($this->config->useHTTPS == true) {
$scheme = "https://";
}
return $scheme . Config::UC_HOST;
}
private function rsPost($path, $body = null)
{
$url = $this->getRsHost() . $path;
return $this->post($url, $body);
}
private function apiPost($path, $body = null)
{
$url = $this->getApiHost() . $path;
return $this->post($url, $body);
}
private function ucPost($path, $body = null)
{
$url = $this->getUcHost() . $path;
return $this->post($url, $body);
}
private function ucGet($path)
{
$url = $this->getUcHost() . $path;
return $this->get($url);
}
private function apiGet($path)
{
$url = $this->getApiHost() . $path;
return $this->get($url);
}
private function rsGet($path)
{
$url = $this->getRsHost() . $path;
return $this->get($url);
}
private function get($url)
{
$headers = $this->auth->authorization($url);
$ret = Client::get($url, $headers);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
return array($ret->json(), null);
}
private function post($url, $body)
{
$headers = $this->auth->authorization($url, $body, 'application/x-www-form-urlencoded');
$ret = Client::post($url, $body, $headers);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
private function ucPostV2($path, $body)
{
$url = $this->getUcHost() . $path;
return $this->postV2($url, $body);
}
private function postV2($url, $body)
{
$headers = $this->auth->authorizationV2($url, 'POST', $body, 'application/json');
$headers["Content-Type"] = 'application/json';
$ret = Client::post($url, $body, $headers);
if (!$ret->ok()) {
return array(null, new Error($url, $ret));
}
$r = ($ret->body === null) ? array() : $ret->json();
return array($r, null);
}
public static function buildBatchCopy($source_bucket, $key_pairs, $target_bucket, $force)
{
return self::twoKeyBatch('/copy', $source_bucket, $key_pairs, $target_bucket, $force);
}
public static function buildBatchRename($bucket, $key_pairs, $force)
{
return self::buildBatchMove($bucket, $key_pairs, $bucket, $force);
}
public static function buildBatchMove($source_bucket, $key_pairs, $target_bucket, $force)
{
return self::twoKeyBatch('/move', $source_bucket, $key_pairs, $target_bucket, $force);
}
public static function buildBatchDelete($bucket, $keys)
{
return self::oneKeyBatch('/delete', $bucket, $keys);
}
public static function buildBatchStat($bucket, $keys)
{
return self::oneKeyBatch('/stat', $bucket, $keys);
}
public static function buildBatchDeleteAfterDays($bucket, $key_day_pairs)
{
$data = array();
foreach ($key_day_pairs as $key => $day) {
array_push($data, '/deleteAfterDays/' . \Qiniu\entry($bucket, $key) . '/' . $day);
}
return $data;
}
public static function buildBatchChangeMime($bucket, $key_mime_pairs)
{
$data = array();
foreach ($key_mime_pairs as $key => $mime) {
array_push($data, '/chgm/' . \Qiniu\entry($bucket, $key) . '/mime/' . base64_encode($mime));
}
return $data;
}
public static function buildBatchChangeType($bucket, $key_type_pairs)
{
$data = array();
foreach ($key_type_pairs as $key => $type) {
array_push($data, '/chtype/' . \Qiniu\entry($bucket, $key) . '/type/' . $type);
}
return $data;
}
private static function oneKeyBatch($operation, $bucket, $keys)
{
$data = array();
foreach ($keys as $key) {
array_push($data, $operation . '/' . \Qiniu\entry($bucket, $key));
}
return $data;
}
private static function twoKeyBatch($operation, $source_bucket, $key_pairs, $target_bucket, $force)
{
if ($target_bucket === null) {
$target_bucket = $source_bucket;
}
$data = array();
$forceOp = "false";
if ($force) {
$forceOp = "true";
}
foreach ($key_pairs as $from_key => $to_key) {
$from = \Qiniu\entry($source_bucket, $from_key);
$to = \Qiniu\entry($target_bucket, $to_key);
array_push($data, $operation . '/' . $from . '/' . $to . "/force/" . $forceOp);
}
return $data;
}
}
... ...
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
final class FormUploader
{
/**
* 上传二进制流到七牛, 内部使用
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param resource $data 上传二进制流
* @param Config $config 上传配置
* @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param string $mime 上传数据的mimeType
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public static function put($upToken, $key, $data, $config, $params, $mime, $fname)
{
$fields = array('token' => $upToken);
if ($key === null) {
} else {
$fields['key'] = $key;
}
//enable crc32 check by default
$fields['crc32'] = \Qiniu\crc32_data($data);
if ($params) {
foreach ($params as $k => $v) {
$fields[$k] = $v;
}
}
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
if ($err != null) {
return array(null, $err);
}
$upHost = $config->getUpHost($accessKey, $bucket);
$response = Client::multipartPost($upHost, $fields, 'file', $fname, $data, $mime);
if (!$response->ok()) {
return array(null, new Error($upHost, $response));
}
return array($response->json(), null);
}
/**
* 上传文件到七牛,内部使用
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $filePath 上传文件的路径
* @param Config $config 上传配置
* @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param string $mime 上传数据的mimeType
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public static function putFile($upToken, $key, $filePath, $config, $params, $mime)
{
$fields = array('token' => $upToken, 'file' => self::createFile($filePath, $mime));
if ($key !== null) {
$fields['key'] = $key;
}
$fields['crc32'] = \Qiniu\crc32_file($filePath);
if ($params) {
foreach ($params as $k => $v) {
$fields[$k] = $v;
}
}
$fields['key'] = $key;
$headers = array('Content-Type' => 'multipart/form-data');
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
if ($err != null) {
return array(null, $err);
}
$upHost = $config->getUpHost($accessKey, $bucket);
$response = Client::post($upHost, $fields, $headers);
if (!$response->ok()) {
return array(null, new Error($upHost, $response));
}
return array($response->json(), null);
}
private static function createFile($filename, $mime)
{
// PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax
// See: https://wiki.php.net/rfc/curl-file-upload
if (function_exists('curl_file_create')) {
return curl_file_create($filename, $mime);
}
// Use the old style if using an older version of PHP
$value = "@{$filename}";
if (!empty($mime)) {
$value .= ';type=' . $mime;
}
return $value;
}
}
... ...
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\Client;
use Qiniu\Http\Error;
/**
* 断点续上传类, 该类主要实现了断点续上传中的分块上传,
* 以及相应地创建块和创建文件过程.
*
* @link http://developer.qiniu.com/docs/v6/api/reference/up/mkblk.html
* @link http://developer.qiniu.com/docs/v6/api/reference/up/mkfile.html
*/
final class ResumeUploader
{
private $upToken;
private $key;
private $inputStream;
private $size;
private $params;
private $mime;
private $contexts;
private $host;
private $currentUrl;
private $config;
/**
* 上传二进制流到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param resource $inputStream 上传二进制流
* @param int $size 上传流的大小
* @param array $params 自定义变量
* @param string $mime 上传数据的mimeType
*
* @link http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
*/
public function __construct(
$upToken,
$key,
$inputStream,
$size,
$params = null,
$mime = '',
$config = null
) {
$this->upToken = $upToken;
$this->key = $key;
$this->inputStream = $inputStream;
$this->size = $size;
$this->params = $params;
$this->mime = $mime ? $mime : 'application/octet-stream';
$this->contexts = array();
$this->config = $config ? $config : new Config();
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($upToken);
if ($err != null) {
return array(null, $err);
}
$upHost = $this->config->getUpHost($accessKey, $bucket);
if ($err != null) {
throw new \Exception($err->message(), 1);
}
$this->host = $upHost;
}
/**
* 上传操作
*/
public function upload($fname)
{
$uploaded = 0;
while ($uploaded < $this->size) {
$blockSize = $this->blockSize($uploaded);
$data = fread($this->inputStream, $blockSize);
if ($data === false) {
throw new \Exception("file read failed", 1);
}
$crc = \Qiniu\crc32_data($data);
$response = $this->makeBlock($data, $blockSize);
$ret = null;
if ($response->ok() && $response->json() != null) {
$ret = $response->json();
}
if ($response->statusCode < 0) {
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
if ($err != null) {
return array(null, $err);
}
$upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);
$this->host = $upHostBackup;
}
if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
$response = $this->makeBlock($data, $blockSize);
$ret = $response->json();
}
if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
return array(null, new Error($this->currentUrl, $response));
}
array_push($this->contexts, $ret['ctx']);
$uploaded += $blockSize;
}
return $this->makeFile($fname);
}
public function uploadChunk($index, $file, $size)
{
$blockSize = $this->size;
$data = fread($this->inputStream, $size);
if ($data === false) {
throw new \Exception("file read failed", 1);
}
$crc = \Qiniu\crc32_data($data);
$response = $this->makeBlock($data, $blockSize);
$ret = null;
if ($response->ok() && $response->json() != null) {
$ret = $response->json();
}
if ($response->statusCode < 0) {
list($accessKey, $bucket, $err) = \Qiniu\explodeUpToken($this->upToken);
if ($err != null) {
return array(null, $err);
}
$upHostBackup = $this->config->getUpBackupHost($accessKey, $bucket);
$this->host = $upHostBackup;
}
if ($response->needRetry() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
$response = $this->makeBlock($data, $blockSize);
$ret = $response->json();
}
if (!$response->ok() || !isset($ret['crc32']) || $crc != $ret['crc32']) {
return array(null, new Error($this->currentUrl, $response));
}
array_push($this->contexts, $ret['ctx']);
return $ret;
}
public function setContexts($contexts)
{
$this->contexts = is_array($contexts) ? $contexts : explode(',', $contexts);
return $this;
}
/**
* 创建块
*/
private function makeBlock($block, $blockSize)
{
$url = $this->host . '/mkblk/' . $blockSize;
return $this->post($url, $block);
}
private function fileUrl($fname)
{
$url = $this->host . '/mkfile/' . $this->size;
$url .= '/mimeType/' . \Qiniu\base64_urlSafeEncode($this->mime);
if ($this->key != null) {
$url .= '/key/' . \Qiniu\base64_urlSafeEncode($this->key);
}
$url .= '/fname/' . \Qiniu\base64_urlSafeEncode($fname);
if (!empty($this->params)) {
foreach ($this->params as $key => $value) {
$val = \Qiniu\base64_urlSafeEncode($value);
$url .= "/$key/$val";
}
}
return $url;
}
/**
* 创建文件
*/
public function makeFile($fname)
{
$url = $this->fileUrl($fname);
$body = implode(',', $this->contexts);
$response = $this->post($url, $body);
if ($response->needRetry()) {
$response = $this->post($url, $body);
}
if (!$response->ok()) {
return array(null, new Error($this->currentUrl, $response));
}
return array($response->json(), null);
}
private function post($url, $data)
{
$this->currentUrl = $url;
$headers = array('Authorization' => 'UpToken ' . $this->upToken);
return Client::post($url, $data, $headers);
}
private function blockSize($uploaded)
{
if ($this->size < $uploaded + Config::BLOCK_SIZE) {
return $this->size - $uploaded;
}
return Config::BLOCK_SIZE;
}
}
... ...
<?php
namespace Qiniu\Storage;
use Qiniu\Config;
use Qiniu\Http\HttpClient;
use Qiniu\Storage\ResumeUploader;
use Qiniu\Storage\FormUploader;
/**
* 主要涉及了资源上传接口的实现
*
* @link http://developer.qiniu.com/docs/v6/api/reference/up/
*/
final class UploadManager
{
private $config;
public function __construct(Config $config = null)
{
if ($config === null) {
$config = new Config();
}
$this->config = $config;
}
/**
* 上传二进制流到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param resource $data 上传二进制流
* @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param string $mime 上传数据的mimeType
* @param bool $checkCrc 是否校验crc32
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public function put($upToken, $key, $data, $params = null, $mime = 'application/octet-stream', $fname = "default_filename")
{
$params = self::trimParams($params);
return FormUploader::put($upToken, $key, $data, $this->config, $params, $mime, $fname);
}
/**
* 上传文件到七牛
*
* @param string $upToken 上传凭证
* @param string $key 上传文件名
* @param string $filePath 上传文件的路径
* @param array $params 自定义变量,规格参考 http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar
* @param string $mime 上传数据的mimeType
* @param bool $checkCrc 是否校验crc32
*
* @return array 包含已上传文件的信息,类似:
* [
* "hash" => "<Hash string>",
* "key" => "<Key string>"
* ]
*/
public function putFile($upToken, $key, $filePath, $params = null, $mime = 'application/octet-stream', $checkCrc = false)
{
$file = fopen($filePath, 'rb');
if ($file === false) {
throw new \Exception("file can not open", 1);
}
$params = self::trimParams($params);
$stat = fstat($file);
$size = $stat['size'];
//不满足分片上传条件
if ($size <= Config::BLOCK_SIZE) {
$data = fread($file, $size);
fclose($file);
if ($data === false) {
throw new \Exception("file can not read", 1);
}
return FormUploader::put($upToken, $key, $data, $this->config, $params, $mime, basename($filePath));
} else {
$up = new ResumeUploader($upToken, $key, $file, $size, $params, $mime, $this->config);
$ret = $up->upload(basename($filePath));
fclose($file);
return $ret;
}
}
public static function trimParams($params)
{
if ($params === null) {
return null;
}
$ret = array();
foreach ($params as $k => $v) {
$pos1 = strpos($k, 'x:');
$pos2 = strpos($k, 'x-qn-meta-');
if (($pos1 === 0 || $pos2 === 0) && !empty($v)) {
$ret[$k] = $v;
}
}
return $ret;
}
}
... ...
<?php
namespace Qiniu;
use Qiniu\Region;
class Zone extends Region
{
public static function zonez0()
{
return parent::regionHuadong();
}
public static function zonez1()
{
return parent::regionHuabei();
}
public static function zonez2()
{
return parent::regionHuanan();
}
public static function zoneAs0()
{
return parent::regionSingapore();
}
public static function zoneNa0()
{
return parent::regionNorthAmerica();
}
public static function qvmZonez0()
{
return parent::qvmRegionHuadong();
}
public static function qvmZonez1()
{
return parent::qvmRegionHuabei();
}
public static function queryZone($ak, $bucket)
{
return parent::queryRegion($ak, $bucket);
}
}
... ...
<?php
namespace Qiniu;
use Qiniu\Config;
if (!defined('QINIU_FUNCTIONS_VERSION')) {
define('QINIU_FUNCTIONS_VERSION', Config::SDK_VER);
/**
* 计算文件的crc32检验码:
*
* @param $file string 待计算校验码的文件路径
*
* @return string 文件内容的crc32校验码
*/
function crc32_file($file)
{
$hash = hash_file('crc32b', $file);
$array = unpack('N', pack('H*', $hash));
return sprintf('%u', $array[1]);
}
/**
* 计算输入流的crc32检验码
*
* @param $data 待计算校验码的字符串
*
* @return string 输入字符串的crc32校验码
*/
function crc32_data($data)
{
$hash = hash('crc32b', $data);
$array = unpack('N', pack('H*', $hash));
return sprintf('%u', $array[1]);
}
/**
* 对提供的数据进行urlsafe的base64编码。
*
* @param string $data 待编码的数据,一般为字符串
*
* @return string 编码后的字符串
* @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64
*/
function base64_urlSafeEncode($data)
{
$find = array('+', '/');
$replace = array('-', '_');
return str_replace($find, $replace, base64_encode($data));
}
/**
* 对提供的urlsafe的base64编码的数据进行解码
*
* @param string $str 待解码的数据,一般为字符串
*
* @return string 解码后的字符串
*/
function base64_urlSafeDecode($str)
{
$find = array('-', '_');
$replace = array('+', '/');
return base64_decode(str_replace($find, $replace, $str));
}
/**
* Wrapper for JSON decode that implements error detection with helpful
* error messages.
*
* @param string $json JSON data to parse
* @param bool $assoc When true, returned objects will be converted
* into associative arrays.
* @param int $depth User specified recursion depth.
*
* @return mixed
* @throws \InvalidArgumentException if the JSON cannot be parsed.
* @link http://www.php.net/manual/en/function.json-decode.php
*/
function json_decode($json, $assoc = false, $depth = 512)
{
static $jsonErrors = array(
JSON_ERROR_DEPTH => 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
);
if (empty($json)) {
return null;
}
$data = \json_decode($json, $assoc, $depth);
if (JSON_ERROR_NONE !== json_last_error()) {
$last = json_last_error();
throw new \InvalidArgumentException(
'Unable to parse JSON data: '
. (isset($jsonErrors[$last])
? $jsonErrors[$last]
: 'Unknown error')
);
}
return $data;
}
/**
* 计算七牛API中的数据格式
*
* @param $bucket 待操作的空间名
* @param $key 待操作的文件名
*
* @return string 符合七牛API规格的数据格式
* @link http://developer.qiniu.com/docs/v6/api/reference/data-formats.html
*/
function entry($bucket, $key)
{
$en = $bucket;
if (!empty($key)) {
$en = $bucket . ':' . $key;
}
return base64_urlSafeEncode($en);
}
/**
* array 辅助方法,无值时不set
*
* @param $array 待操作array
* @param $key key
* @param $value value 为null时 不设置
*
* @return array 原来的array,便于连续操作
*/
function setWithoutEmpty(&$array, $key, $value)
{
if (!empty($value)) {
$array[$key] = $value;
}
return $array;
}
/**
* 缩略图链接拼接
*
* @param string $url 图片链接
* @param int $mode 缩略模式
* @param int $width 宽度
* @param int $height 长度
* @param string $format 输出类型
* @param int $quality 图片质量
* @param int $interlace 是否支持渐进显示
* @param int $ignoreError 忽略结果
* @return string
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function thumbnail(
$url,
$mode,
$width,
$height,
$format = null,
$quality = null,
$interlace = null,
$ignoreError = 1
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'thumbnail'), func_get_args());
}
/**
* 图片水印
*
* @param string $url 图片链接
* @param string $image 水印图片链接
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @param numeric $watermarkScale 自适应原图的短边比例
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function waterImg(
$url,
$image,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null,
$watermarkScale = null
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'waterImg'), func_get_args());
}
/**
* 文字水印
*
* @param string $url 图片链接
* @param string $text 文字
* @param string $font 文字字体
* @param string $fontSize 文字字号
* @param string $fontColor 文字颜色
* @param numeric $dissolve 透明度
* @param string $gravity 水印位置
* @param numeric $dx 横轴边距
* @param numeric $dy 纵轴边距
* @link http://developer.qiniu.com/code/v6/api/kodo-api/image/watermark.html#text-watermark
* @return string
* @author Sherlock Ren <sherlock_ren@icloud.com>
*/
function waterText(
$url,
$text,
$font = '黑体',
$fontSize = 0,
$fontColor = null,
$dissolve = 100,
$gravity = 'SouthEast',
$dx = null,
$dy = null
) {
static $imageUrlBuilder = null;
if (is_null($imageUrlBuilder)) {
$imageUrlBuilder = new \Qiniu\Processing\ImageUrlBuilder;
}
return call_user_func_array(array($imageUrlBuilder, 'waterText'), func_get_args());
}
/**
* 从uptoken解析accessKey和bucket
*
* @param $upToken
* @return array(ak,bucket,err=null)
*/
function explodeUpToken($upToken)
{
$items = explode(':', $upToken);
if (count($items) != 3) {
return array(null, null, "invalid uptoken");
}
$accessKey = $items[0];
$putPolicy = json_decode(base64_urlSafeDecode($items[2]));
$scope = $putPolicy->scope;
$scopeItems = explode(':', $scope);
$bucket = $scopeItems[0];
return array($accessKey, $bucket, null);
}
}
... ...