├── Plugin.php ├── README.md └── sdk ├── auth_digest.php ├── conf.php ├── fop.php ├── http.php ├── io.php ├── resumable_io.php ├── rs.php ├── rs_utils.php ├── rsf.php └── utils.php /Plugin.php: -------------------------------------------------------------------------------- 1 | 源代码参考 & 注册七牛 4 | * 5 | * @package Qiniu File 6 | * @author abelyao 7 | * @version 1.2.0 8 | * @link http://www.abelyao.com/ 9 | * @date 2014-02-22 10 | */ 11 | 12 | class QiniuFile_Plugin implements Typecho_Plugin_Interface 13 | { 14 | // 激活插件 15 | public static function activate() 16 | { 17 | Typecho_Plugin::factory('Widget_Upload')->uploadHandle = array('QiniuFile_Plugin', 'uploadHandle'); 18 | Typecho_Plugin::factory('Widget_Upload')->modifyHandle = array('QiniuFile_Plugin', 'modifyHandle'); 19 | Typecho_Plugin::factory('Widget_Upload')->deleteHandle = array('QiniuFile_Plugin', 'deleteHandle'); 20 | Typecho_Plugin::factory('Widget_Upload')->attachmentHandle = array('QiniuFile_Plugin', 'attachmentHandle'); 21 | return _t('插件已经激活,需先配置七牛的信息!'); 22 | } 23 | 24 | 25 | // 禁用插件 26 | public static function deactivate() 27 | { 28 | return _t('插件已被禁用'); 29 | } 30 | 31 | 32 | // 插件配置面板 33 | public static function config(Typecho_Widget_Helper_Form $form) 34 | { 35 | $bucket = new Typecho_Widget_Helper_Form_Element_Text('bucket', null, null, _t('空间名称:')); 36 | $form->addInput($bucket->addRule('required', _t('“空间名称”不能为空!'))); 37 | 38 | $accesskey = new Typecho_Widget_Helper_Form_Element_Text('accesskey', null, null, _t('AccessKey:')); 39 | $form->addInput($accesskey->addRule('required', _t('AccessKey 不能为空!'))); 40 | 41 | $sercetkey = new Typecho_Widget_Helper_Form_Element_Text('sercetkey', null, null, _t('SecretKey:')); 42 | $form->addInput($sercetkey->addRule('required', _t('SecretKey 不能为空!'))); 43 | 44 | $domain = new Typecho_Widget_Helper_Form_Element_Text('domain', null, 'http://', _t('绑定域名:'), _t('以 http:// 开头,结尾不要加 / !')); 45 | $form->addInput($domain->addRule('required', _t('请填写空间绑定的域名!'))->addRule('url', _t('您输入的域名格式错误!'))); 46 | 47 | $savepath = new Typecho_Widget_Helper_Form_Element_Text('savepath', null, '{year}/{month}/', _t('保存路径格式:'), _t('附件保存路径的格式,默认为 Typecho 的 {year}/{month}/ 格式,注意前面不要加 /
可选参数:{year} 年份、{month} 月份、{day} 日期')); 48 | $form->addInput($savepath->addRule('required', _t('请填写保存路径格式!'))); 49 | } 50 | 51 | 52 | // 个人用户配置面板 53 | public static function personalConfig(Typecho_Widget_Helper_Form $form) 54 | { 55 | } 56 | 57 | 58 | // 获得插件配置信息 59 | public static function getConfig() 60 | { 61 | return Typecho_Widget::widget('Widget_Options')->plugin('QiniuFile'); 62 | } 63 | 64 | 65 | // 初始化七牛SDK 66 | public static function initSDK($accesskey, $sercetkey) 67 | { 68 | // 调用 SDK 设置密钥 69 | require_once 'sdk/io.php'; 70 | require_once 'sdk/rs.php'; 71 | require_once 'sdk/rsf.php'; 72 | Qiniu_SetKeys($accesskey, $sercetkey); 73 | } 74 | 75 | 76 | // 删除文件 77 | public static function deleteFile($filepath) 78 | { 79 | // 获取插件配置 80 | $option = self::getConfig(); 81 | 82 | // 初始化 SDK 83 | self::initSDK($option->accesskey, $option->sercetkey); 84 | 85 | // 删除 86 | $client = new Qiniu_MacHttpClient(null); 87 | return Qiniu_RS_Delete($client, $option->bucket, $filepath); 88 | } 89 | 90 | 91 | // 上传文件 92 | public static function uploadFile($file, $content = null) 93 | { 94 | // 获取上传文件 95 | if (empty($file['name'])) return false; 96 | 97 | // 校验扩展名 98 | $part = explode('.', $file['name']); 99 | $ext = (($length = count($part)) > 1) ? strtolower($part[$length-1]) : ''; 100 | if (!Widget_Upload::checkFileType($ext)) return false; 101 | 102 | // 获取插件配置 103 | $option = self::getConfig(); 104 | $date = new Typecho_Date(Typecho_Widget::widget('Widget_Options')->gmtTime); 105 | 106 | // 保存位置 107 | $savepath = preg_replace(array('/\{year\}/', '/\{month\}/', '/\{day\}/'), array($date->year, $date->month, $date->day), $option->savepath); 108 | $savename = $savepath . sprintf('%u', crc32(uniqid())) . '.' . $ext; 109 | if (isset($content)) 110 | { 111 | $savename = $content['attachment']->path; 112 | self::deleteFile($savename); 113 | } 114 | 115 | // 上传文件 116 | $filename = $file['tmp_name']; 117 | if (!isset($filename)) return false; 118 | 119 | // 初始化 SDK 120 | self::initSDK($option->accesskey, $option->sercetkey); 121 | 122 | // 上传凭证 123 | $policy = new Qiniu_RS_PutPolicy($option->bucket); 124 | $token = $policy->Token(null); 125 | $extra = new Qiniu_PutExtra(); 126 | $extra->Crc32 = 1; 127 | 128 | // 上传 129 | list($result, $error) = Qiniu_PutFile($token, $savename, $filename, $extra); 130 | 131 | if ($error == null) 132 | { 133 | return array 134 | ( 135 | 'name' => $file['name'], 136 | 'path' => $savename, 137 | 'size' => $file['size'], 138 | 'type' => $ext, 139 | 'mime' => Typecho_Common::mimeContentType($savename) 140 | ); 141 | } 142 | else return false; 143 | } 144 | 145 | 146 | // 上传文件处理函数 147 | public static function uploadHandle($file) 148 | { 149 | return self::uploadFile($file); 150 | } 151 | 152 | 153 | // 修改文件处理函数 154 | public static function modifyHandle($content, $file) 155 | { 156 | return self::uploadFile($file, $content); 157 | } 158 | 159 | 160 | // 删除文件 161 | public static function deleteHandle(array $content) 162 | { 163 | self::deleteFile($content['attachment']->path); 164 | } 165 | 166 | 167 | // 获取实际文件绝对访问路径 168 | public static function attachmentHandle(array $content) 169 | { 170 | $option = self::getConfig(); 171 | return Typecho_Common::url($content['attachment']->path, $option->domain); 172 | } 173 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # typecho附件七牛云上传 2 | 3 | TODO: 4 | 七牛云文件管理 5 | ****** 6 | 7 | >七牛云提供文件存储,音频处理,文档转换,CDN等服务。 —— [官网](https://www.qiniu.com) 8 | 9 | > 参考[七牛云存储使用指南] —— [七牛云文档](http://developer.qiniu.com/) 10 | 11 | **使用方法** 12 | - 上传至:/usr/plugins 13 | - 启用插件 14 | - 填写配置信息,保存 15 | - 开始撰写文章体验附件上传 16 | 17 | 18 | ###请注意: 19 | >文件夹名应为:QiniuFile,与plugin.php中的QiniuFile_Plugin一致, 20 | 21 | --------- 22 | 感谢使用。若觉得不错,别忘了**star**。 23 | -------------------------------------------------------------------------------- /sdk/auth_digest.php: -------------------------------------------------------------------------------- 1 | AccessKey = $accessKey; 16 | $this->SecretKey = $secretKey; 17 | } 18 | 19 | public function Sign($data) // => $token 20 | { 21 | $sign = hash_hmac('sha1', $data, $this->SecretKey, true); 22 | return $this->AccessKey . ':' . Qiniu_Encode($sign); 23 | } 24 | 25 | public function SignWithData($data) // => $token 26 | { 27 | $data = Qiniu_Encode($data); 28 | return $this->Sign($data) . ':' . $data; 29 | } 30 | 31 | public function SignRequest($req, $incbody) // => ($token, $error) 32 | { 33 | $url = $req->URL; 34 | $url = parse_url($url['path']); 35 | $data = ''; 36 | if (isset($url['path'])) { 37 | $data = $url['path']; 38 | } 39 | if (isset($url['query'])) { 40 | $data .= '?' . $url['query']; 41 | } 42 | $data .= "\n"; 43 | 44 | if ($incbody) { 45 | $data .= $req->Body; 46 | } 47 | return $this->Sign($data); 48 | } 49 | 50 | public function VerifyCallback($auth, $url, $body) // ==> bool 51 | { 52 | $url = parse_url($url); 53 | $data = ''; 54 | if (isset($url['path'])) { 55 | $data = $url['path']; 56 | } 57 | if (isset($url['query'])) { 58 | $data .= '?' . $url['query']; 59 | } 60 | $data .= "\n"; 61 | 62 | $data .= $body; 63 | $token = 'QBox ' . $this->Sign($data); 64 | return $auth === $token; 65 | } 66 | } 67 | 68 | function Qiniu_SetKeys($accessKey, $secretKey) 69 | { 70 | global $QINIU_ACCESS_KEY; 71 | global $QINIU_SECRET_KEY; 72 | 73 | $QINIU_ACCESS_KEY = $accessKey; 74 | $QINIU_SECRET_KEY = $secretKey; 75 | } 76 | 77 | function Qiniu_RequireMac($mac) // => $mac 78 | { 79 | if (isset($mac)) { 80 | return $mac; 81 | } 82 | 83 | global $QINIU_ACCESS_KEY; 84 | global $QINIU_SECRET_KEY; 85 | 86 | return new Qiniu_Mac($QINIU_ACCESS_KEY, $QINIU_SECRET_KEY); 87 | } 88 | 89 | function Qiniu_Sign($mac, $data) // => $token 90 | { 91 | return Qiniu_RequireMac($mac)->Sign($data); 92 | } 93 | 94 | function Qiniu_SignWithData($mac, $data) // => $token 95 | { 96 | return Qiniu_RequireMac($mac)->SignWithData($data); 97 | } 98 | 99 | // ---------------------------------------------------------- 100 | 101 | -------------------------------------------------------------------------------- /sdk/conf.php: -------------------------------------------------------------------------------- 1 | '; 19 | $QINIU_SECRET_KEY = ''; 20 | 21 | -------------------------------------------------------------------------------- /sdk/fop.php: -------------------------------------------------------------------------------- 1 | Mode); 18 | 19 | if (!empty($this->Width)) { 20 | $ops[] = 'w/' . $this->Width; 21 | } 22 | if (!empty($this->Height)) { 23 | $ops[] = 'h/' . $this->Height; 24 | } 25 | if (!empty($this->Quality)) { 26 | $ops[] = 'q/' . $this->Quality; 27 | } 28 | if (!empty($this->Format)) { 29 | $ops[] = 'format/' . $this->Format; 30 | } 31 | 32 | return $url . "?imageView/" . implode('/', $ops); 33 | } 34 | } 35 | 36 | // -------------------------------------------------------------------------------- 37 | // class Qiniu_Exif 38 | 39 | class Qiniu_Exif { 40 | 41 | public function MakeRequest($url) 42 | { 43 | return $url . "?exif"; 44 | } 45 | 46 | } 47 | 48 | // -------------------------------------------------------------------------------- 49 | // class Qiniu_ImageInfo 50 | 51 | class Qiniu_ImageInfo { 52 | 53 | public function MakeRequest($url) 54 | { 55 | return $url . "?imageInfo"; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /sdk/http.php: -------------------------------------------------------------------------------- 1 | Code = $code; 19 | $this->Err = $err; 20 | } 21 | } 22 | 23 | // -------------------------------------------------------------------------------- 24 | // class Qiniu_Request 25 | 26 | class Qiniu_Request 27 | { 28 | public $URL; 29 | public $Header; 30 | public $Body; 31 | public $UA; 32 | 33 | public function __construct($url, $body) 34 | { 35 | $this->URL = $url; 36 | $this->Header = array(); 37 | $this->Body = $body; 38 | $this->UA = Qiniu_UserAgent(); 39 | } 40 | } 41 | 42 | // -------------------------------------------------------------------------------- 43 | // class Qiniu_Response 44 | 45 | class Qiniu_Response 46 | { 47 | public $StatusCode; 48 | public $Header; 49 | public $ContentLength; 50 | public $Body; 51 | 52 | public function __construct($code, $body) 53 | { 54 | $this->StatusCode = $code; 55 | $this->Header = array(); 56 | $this->Body = $body; 57 | $this->ContentLength = strlen($body); 58 | } 59 | } 60 | 61 | // -------------------------------------------------------------------------------- 62 | // class Qiniu_Header 63 | 64 | function Qiniu_Header_Get($header, $key) // => $val 65 | { 66 | $val = @$header[$key]; 67 | if (isset($val)) { 68 | if (is_array($val)) { 69 | return $val[0]; 70 | } 71 | return $val; 72 | } else { 73 | return ''; 74 | } 75 | } 76 | 77 | function Qiniu_ResponseError($resp) // => $error 78 | { 79 | $header = $resp->Header; 80 | $details = Qiniu_Header_Get($header, 'X-Log'); 81 | $reqId = Qiniu_Header_Get($header, 'X-Reqid'); 82 | $err = new Qiniu_Error($resp->StatusCode, null); 83 | 84 | if ($err->Code > 299) { 85 | if ($resp->ContentLength !== 0) { 86 | if (Qiniu_Header_Get($header, 'Content-Type') === 'application/json') { 87 | $ret = json_decode($resp->Body, true); 88 | $err->Err = $ret['error']; 89 | } 90 | } 91 | } 92 | $err->Reqid = $reqId; 93 | $err->Details = $details; 94 | return $err; 95 | } 96 | 97 | // -------------------------------------------------------------------------------- 98 | // class Qiniu_Client 99 | 100 | function Qiniu_Client_incBody($req) // => $incbody 101 | { 102 | $body = $req->Body; 103 | if (!isset($body)) { 104 | return false; 105 | } 106 | 107 | $ct = Qiniu_Header_Get($req->Header, 'Content-Type'); 108 | if ($ct === 'application/x-www-form-urlencoded') { 109 | return true; 110 | } 111 | return false; 112 | } 113 | 114 | function Qiniu_Client_do($req) // => ($resp, $error) 115 | { 116 | $ch = curl_init(); 117 | $url = $req->URL; 118 | $options = array( 119 | CURLOPT_USERAGENT => $req->UA, 120 | CURLOPT_RETURNTRANSFER => true, 121 | CURLOPT_SSL_VERIFYPEER => false, 122 | CURLOPT_SSL_VERIFYHOST => false, 123 | CURLOPT_HEADER => true, 124 | CURLOPT_NOBODY => false, 125 | CURLOPT_CUSTOMREQUEST => 'POST', 126 | CURLOPT_URL => $url['path'] 127 | ); 128 | $httpHeader = $req->Header; 129 | if (!empty($httpHeader)) 130 | { 131 | $header = array(); 132 | foreach($httpHeader as $key => $parsedUrlValue) { 133 | $header[] = "$key: $parsedUrlValue"; 134 | } 135 | $options[CURLOPT_HTTPHEADER] = $header; 136 | } 137 | $body = $req->Body; 138 | if (!empty($body)) { 139 | $options[CURLOPT_POSTFIELDS] = $body; 140 | } else { 141 | $options[CURLOPT_POSTFIELDS] = ""; 142 | } 143 | curl_setopt_array($ch, $options); 144 | $result = curl_exec($ch); 145 | $ret = curl_errno($ch); 146 | if ($ret !== 0) { 147 | $err = new Qiniu_Error(0, curl_error($ch)); 148 | curl_close($ch); 149 | return array(null, $err); 150 | } 151 | $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); 152 | $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); 153 | curl_close($ch); 154 | 155 | $responseArray = explode("\r\n\r\n", $result); 156 | $responseArraySize = sizeof($responseArray); 157 | $respHeader = $responseArray[$responseArraySize-2]; 158 | $respBody = $responseArray[$responseArraySize-1]; 159 | 160 | list($reqid, $xLog) = getReqInfo($respHeader); 161 | 162 | $resp = new Qiniu_Response($code, $respBody); 163 | $resp->Header['Content-Type'] = $contentType; 164 | $resp->Header["X-Reqid"] = $reqid; 165 | return array($resp, null); 166 | } 167 | 168 | function getReqInfo($headerContent) { 169 | $headers = explode("\r\n", $headerContent); 170 | $reqid = null; 171 | $xLog = null; 172 | foreach($headers as $header) { 173 | $header = trim($header); 174 | if(strpos($header, 'X-Reqid') !== false) { 175 | list($k, $v) = explode(':', $header); 176 | $reqid = trim($v); 177 | } elseif(strpos($header, 'X-Log') !== false) { 178 | list($k, $v) = explode(':', $header); 179 | $xLog = trim($v); 180 | } 181 | } 182 | return array($reqid, $xLog); 183 | } 184 | 185 | class Qiniu_HttpClient 186 | { 187 | public function RoundTrip($req) // => ($resp, $error) 188 | { 189 | return Qiniu_Client_do($req); 190 | } 191 | } 192 | 193 | class Qiniu_MacHttpClient 194 | { 195 | public $Mac; 196 | 197 | public function __construct($mac) 198 | { 199 | $this->Mac = Qiniu_RequireMac($mac); 200 | } 201 | 202 | public function RoundTrip($req) // => ($resp, $error) 203 | { 204 | $incbody = Qiniu_Client_incBody($req); 205 | $token = $this->Mac->SignRequest($req, $incbody); 206 | $req->Header['Authorization'] = "QBox $token"; 207 | return Qiniu_Client_do($req); 208 | } 209 | } 210 | 211 | // -------------------------------------------------------------------------------- 212 | 213 | function Qiniu_Client_ret($resp) // => ($data, $error) 214 | { 215 | $code = $resp->StatusCode; 216 | $data = null; 217 | if ($code >= 200 && $code <= 299) { 218 | if ($resp->ContentLength !== 0) { 219 | $data = json_decode($resp->Body, true); 220 | if ($data === null) { 221 | $err_msg = function_exists('json_last_error_msg') ? json_last_error_msg() : "error with content:" . $resp->Body; 222 | $err = new Qiniu_Error(0, $err_msg); 223 | return array(null, $err); 224 | } 225 | } 226 | if ($code === 200) { 227 | return array($data, null); 228 | } 229 | } 230 | return array($data, Qiniu_ResponseError($resp)); 231 | } 232 | 233 | function Qiniu_Client_Call($self, $url) // => ($data, $error) 234 | { 235 | $u = array('path' => $url); 236 | $req = new Qiniu_Request($u, null); 237 | list($resp, $err) = $self->RoundTrip($req); 238 | if ($err !== null) { 239 | return array(null, $err); 240 | } 241 | return Qiniu_Client_ret($resp); 242 | } 243 | 244 | function Qiniu_Client_CallNoRet($self, $url) // => $error 245 | { 246 | $u = array('path' => $url); 247 | $req = new Qiniu_Request($u, null); 248 | list($resp, $err) = $self->RoundTrip($req); 249 | if ($err !== null) { 250 | return array(null, $err); 251 | } 252 | if ($resp->StatusCode === 200) { 253 | return null; 254 | } 255 | return Qiniu_ResponseError($resp); 256 | } 257 | 258 | function Qiniu_Client_CallWithForm( 259 | $self, $url, $params, $contentType = 'application/x-www-form-urlencoded') // => ($data, $error) 260 | { 261 | $u = array('path' => $url); 262 | if ($contentType === 'application/x-www-form-urlencoded') { 263 | if (is_array($params)) { 264 | $params = http_build_query($params); 265 | } 266 | } 267 | $req = new Qiniu_Request($u, $params); 268 | if ($contentType !== 'multipart/form-data') { 269 | $req->Header['Content-Type'] = $contentType; 270 | } 271 | list($resp, $err) = $self->RoundTrip($req); 272 | if ($err !== null) { 273 | return array(null, $err); 274 | } 275 | return Qiniu_Client_ret($resp); 276 | } 277 | 278 | // -------------------------------------------------------------------------------- 279 | 280 | function Qiniu_Client_CallWithMultipartForm($self, $url, $fields, $files) 281 | { 282 | list($contentType, $body) = Qiniu_Build_MultipartForm($fields, $files); 283 | return Qiniu_Client_CallWithForm($self, $url, $body, $contentType); 284 | } 285 | 286 | function Qiniu_Build_MultipartForm($fields, $files) // => ($contentType, $body) 287 | { 288 | $data = array(); 289 | $mimeBoundary = md5(microtime()); 290 | 291 | foreach ($fields as $name => $val) { 292 | array_push($data, '--' . $mimeBoundary); 293 | array_push($data, "Content-Disposition: form-data; name=\"$name\""); 294 | array_push($data, ''); 295 | array_push($data, $val); 296 | } 297 | 298 | foreach ($files as $file) { 299 | array_push($data, '--' . $mimeBoundary); 300 | list($name, $fileName, $fileBody, $mimeType) = $file; 301 | $mimeType = empty($mimeType) ? 'application/octet-stream' : $mimeType; 302 | $fileName = Qiniu_escapeQuotes($fileName); 303 | array_push($data, "Content-Disposition: form-data; name=\"$name\"; filename=\"$fileName\""); 304 | array_push($data, "Content-Type: $mimeType"); 305 | array_push($data, ''); 306 | array_push($data, $fileBody); 307 | } 308 | 309 | array_push($data, '--' . $mimeBoundary . '--'); 310 | array_push($data, ''); 311 | 312 | $body = implode("\r\n", $data); 313 | $contentType = 'multipart/form-data; boundary=' . $mimeBoundary; 314 | return array($contentType, $body); 315 | } 316 | 317 | function Qiniu_UserAgent() { 318 | global $SDK_VER; 319 | $sdkInfo = "QiniuPHP/$SDK_VER"; 320 | 321 | $systemInfo = php_uname("s"); 322 | $machineInfo = php_uname("m"); 323 | 324 | $envInfo = "($systemInfo/$machineInfo)"; 325 | 326 | $phpVer = phpversion(); 327 | 328 | $ua = "$sdkInfo $envInfo PHP/$phpVer"; 329 | return $ua; 330 | } 331 | 332 | function Qiniu_escapeQuotes($str) 333 | { 334 | $find = array("\\", "\""); 335 | $replace = array("\\\\", "\\\""); 336 | return str_replace($find, $replace, $str); 337 | } 338 | 339 | // -------------------------------------------------------------------------------- 340 | 341 | -------------------------------------------------------------------------------- /sdk/io.php: -------------------------------------------------------------------------------- 1 | ($putRet, $err) 18 | { 19 | global $QINIU_UP_HOST; 20 | 21 | if ($putExtra === null) { 22 | $putExtra = new Qiniu_PutExtra; 23 | } 24 | 25 | $fields = array('token' => $upToken); 26 | if ($key === null) { 27 | $fname = '?'; 28 | } else { 29 | $fname = $key; 30 | $fields['key'] = $key; 31 | } 32 | if ($putExtra->CheckCrc) { 33 | $fields['crc32'] = $putExtra->Crc32; 34 | } 35 | if ($putExtra->Params) { 36 | foreach ($putExtra->Params as $k=>$v) { 37 | $fields[$k] = $v; 38 | } 39 | } 40 | 41 | $files = array(array('file', $fname, $body, $putExtra->MimeType)); 42 | 43 | $client = new Qiniu_HttpClient; 44 | return Qiniu_Client_CallWithMultipartForm($client, $QINIU_UP_HOST, $fields, $files); 45 | } 46 | 47 | function createFile($filename, $mime) 48 | { 49 | // PHP 5.5 introduced a CurlFile object that deprecates the old @filename syntax 50 | // See: https://wiki.php.net/rfc/curl-file-upload 51 | if (function_exists('curl_file_create')) { 52 | return curl_file_create($filename, $mime); 53 | } 54 | 55 | // Use the old style if using an older version of PHP 56 | $value = "@{$filename}"; 57 | if (!empty($mime)) { 58 | $value .= ';type=' . $mime; 59 | } 60 | 61 | return $value; 62 | } 63 | 64 | function Qiniu_PutFile($upToken, $key, $localFile, $putExtra) // => ($putRet, $err) 65 | { 66 | global $QINIU_UP_HOST; 67 | 68 | if ($putExtra === null) { 69 | $putExtra = new Qiniu_PutExtra; 70 | } 71 | 72 | $fields = array('token' => $upToken, 'file' => createFile($localFile, $putExtra->MimeType)); 73 | if ($key === null) { 74 | $fname = '?'; 75 | } else { 76 | $fname = $key; 77 | $fields['key'] = $key; 78 | } 79 | if ($putExtra->CheckCrc) { 80 | if ($putExtra->CheckCrc === 1) { 81 | $hash = hash_file('crc32b', $localFile); 82 | $array = unpack('N', pack('H*', $hash)); 83 | $putExtra->Crc32 = $array[1]; 84 | } 85 | $fields['crc32'] = sprintf('%u', $putExtra->Crc32); 86 | } 87 | if ($putExtra->Params) { 88 | foreach ($putExtra->Params as $k=>$v) { 89 | $fields[$k] = $v; 90 | } 91 | } 92 | 93 | $client = new Qiniu_HttpClient; 94 | return Qiniu_Client_CallWithForm($client, $QINIU_UP_HOST, $fields, 'multipart/form-data'); 95 | } 96 | 97 | // ---------------------------------------------------------- 98 | 99 | -------------------------------------------------------------------------------- /sdk/resumable_io.php: -------------------------------------------------------------------------------- 1 | Bucket = $bucket; 22 | } 23 | } 24 | 25 | // ---------------------------------------------------------- 26 | // func Qiniu_Rio_BlockCount 27 | 28 | define('QINIU_RIO_BLOCK_BITS', 22); 29 | define('QINIU_RIO_BLOCK_SIZE', 1 << QINIU_RIO_BLOCK_BITS); // 4M 30 | 31 | function Qiniu_Rio_BlockCount($fsize) // => $blockCnt 32 | { 33 | return ($fsize + (QINIU_RIO_BLOCK_SIZE - 1)) >> QINIU_RIO_BLOCK_BITS; 34 | } 35 | 36 | // ---------------------------------------------------------- 37 | // internal func Qiniu_Rio_Mkblock/Mkfile 38 | 39 | function Qiniu_Rio_Mkblock($self, $host, $reader, $size) // => ($blkputRet, $err) 40 | { 41 | if (is_resource($reader)) { 42 | $body = fread($reader, $size); 43 | if ($body === false) { 44 | $err = new Qiniu_Error(0, 'fread failed'); 45 | return array(null, $err); 46 | } 47 | } else { 48 | list($body, $err) = $reader->Read($size); 49 | if ($err !== null) { 50 | return array(null, $err); 51 | } 52 | } 53 | if (strlen($body) != $size) { 54 | $err = new Qiniu_Error(0, 'fread failed: unexpected eof'); 55 | return array(null, $err); 56 | } 57 | 58 | $url = $host . '/mkblk/' . $size; 59 | return Qiniu_Client_CallWithForm($self, $url, $body, 'application/octet-stream'); 60 | } 61 | 62 | 63 | function Qiniu_Rio_Mkfile($self, $host, $key, $fsize, $extra) // => ($putRet, $err) 64 | { 65 | $url = $host . '/mkfile/' . $fsize; 66 | if ($key !== null) { 67 | $url .= '/key/' . Qiniu_Encode($key); 68 | } 69 | if (!empty($extra->MimeType)) { 70 | $url .= '/mimeType/' . Qiniu_Encode($extra->MimeType); 71 | } 72 | 73 | if (!empty($extra->Params)) { 74 | foreach ($extra->Params as $k=>$v) { 75 | $url .= "/" . $k . "/" . Qiniu_Encode($v); 76 | } 77 | } 78 | 79 | $ctxs = array(); 80 | foreach ($extra->Progresses as $prog) { 81 | $ctxs []= $prog['ctx']; 82 | } 83 | $body = implode(',', $ctxs); 84 | 85 | return Qiniu_Client_CallWithForm($self, $url, $body, 'application/octet-stream'); 86 | } 87 | 88 | // ---------------------------------------------------------- 89 | // class Qiniu_Rio_UploadClient 90 | 91 | class Qiniu_Rio_UploadClient 92 | { 93 | public $uptoken; 94 | 95 | public function __construct($uptoken) 96 | { 97 | $this->uptoken = $uptoken; 98 | } 99 | 100 | public function RoundTrip($req) // => ($resp, $error) 101 | { 102 | $token = $this->uptoken; 103 | $req->Header['Authorization'] = "UpToken $token"; 104 | return Qiniu_Client_do($req); 105 | } 106 | } 107 | 108 | // ---------------------------------------------------------- 109 | // class Qiniu_Rio_Put/PutFile 110 | 111 | function Qiniu_Rio_Put($upToken, $key, $body, $fsize, $putExtra) // => ($putRet, $err) 112 | { 113 | global $QINIU_UP_HOST; 114 | 115 | $self = new Qiniu_Rio_UploadClient($upToken); 116 | 117 | $progresses = array(); 118 | $uploaded = 0; 119 | while ($uploaded < $fsize) { 120 | $tried = 0; 121 | $tryTimes = ($putExtra->TryTimes > 0) ? $putExtra->TryTimes : 1; 122 | $blkputRet = null; 123 | $err = null; 124 | if ($fsize < $uploaded + QINIU_RIO_BLOCK_SIZE) { 125 | $bsize = $fsize - $uploaded; 126 | } else { 127 | $bsize = QINIU_RIO_BLOCK_SIZE; 128 | } 129 | while ($tried < $tryTimes) { 130 | list($blkputRet, $err) = Qiniu_Rio_Mkblock($self, $QINIU_UP_HOST, $body, $bsize); 131 | if ($err === null) { 132 | break; 133 | } 134 | $tried += 1; 135 | continue; 136 | } 137 | if ($err !== null) { 138 | return array(null, $err); 139 | } 140 | if ($blkputRet === null ) { 141 | $err = new Qiniu_Error(0, "rio: uploaded without ret"); 142 | return array(null, $err); 143 | } 144 | $uploaded += $bsize; 145 | $progresses []= $blkputRet; 146 | } 147 | 148 | $putExtra->Progresses = $progresses; 149 | return Qiniu_Rio_Mkfile($self, $QINIU_UP_HOST, $key, $fsize, $putExtra); 150 | } 151 | 152 | function Qiniu_Rio_PutFile($upToken, $key, $localFile, $putExtra) // => ($putRet, $err) 153 | { 154 | $fp = fopen($localFile, 'rb'); 155 | if ($fp === false) { 156 | $err = new Qiniu_Error(0, 'fopen failed'); 157 | return array(null, $err); 158 | } 159 | 160 | $fi = fstat($fp); 161 | $result = Qiniu_Rio_Put($upToken, $key, $fp, $fi['size'], $putExtra); 162 | fclose($fp); 163 | return $result; 164 | } 165 | 166 | // ---------------------------------------------------------- 167 | 168 | -------------------------------------------------------------------------------- /sdk/rs.php: -------------------------------------------------------------------------------- 1 | $privateUrl 13 | { 14 | $deadline = $this->Expires; 15 | if ($deadline == 0) { 16 | $deadline = 3600; 17 | } 18 | $deadline += time(); 19 | 20 | $pos = strpos($baseUrl, '?'); 21 | if ($pos !== false) { 22 | $baseUrl .= '&e='; 23 | } else { 24 | $baseUrl .= '?e='; 25 | } 26 | $baseUrl .= $deadline; 27 | 28 | $token = Qiniu_Sign($mac, $baseUrl); 29 | return "$baseUrl&token=$token"; 30 | } 31 | } 32 | 33 | function Qiniu_RS_MakeBaseUrl($domain, $key) // => $baseUrl 34 | { 35 | $keyEsc = str_replace("%2F", "/", rawurlencode($key)); 36 | return "http://$domain/$keyEsc"; 37 | } 38 | 39 | // -------------------------------------------------------------------------------- 40 | // class Qiniu_RS_PutPolicy 41 | 42 | class Qiniu_RS_PutPolicy 43 | { 44 | public $Scope; //必填 45 | public $Expires; //默认为3600s 46 | public $CallbackUrl; 47 | public $CallbackBody; 48 | public $ReturnUrl; 49 | public $ReturnBody; 50 | public $AsyncOps; 51 | public $EndUser; 52 | public $InsertOnly; //若非0,则任何情况下无法覆盖上传 53 | public $DetectMime; //若非0,则服务端根据内容自动确定MimeType 54 | public $FsizeLimit; 55 | public $SaveKey; 56 | public $PersistentOps; 57 | public $PersistentPipeline; 58 | public $PersistentNotifyUrl; 59 | public $FopTimeout; 60 | public $MimeLimit; 61 | 62 | public function __construct($scope) 63 | { 64 | $this->Scope = $scope; 65 | } 66 | 67 | public function Token($mac) // => $token 68 | { 69 | $deadline = $this->Expires; 70 | if ($deadline == 0) { 71 | $deadline = 3600; 72 | } 73 | $deadline += time(); 74 | 75 | $policy = array('scope' => $this->Scope, 'deadline' => $deadline); 76 | if (!empty($this->CallbackUrl)) { 77 | $policy['callbackUrl'] = $this->CallbackUrl; 78 | } 79 | if (!empty($this->CallbackBody)) { 80 | $policy['callbackBody'] = $this->CallbackBody; 81 | } 82 | if (!empty($this->ReturnUrl)) { 83 | $policy['returnUrl'] = $this->ReturnUrl; 84 | } 85 | if (!empty($this->ReturnBody)) { 86 | $policy['returnBody'] = $this->ReturnBody; 87 | } 88 | if (!empty($this->AsyncOps)) { 89 | $policy['asyncOps'] = $this->AsyncOps; 90 | } 91 | if (!empty($this->EndUser)) { 92 | $policy['endUser'] = $this->EndUser; 93 | } 94 | if (!empty($this->InsertOnly)) { 95 | $policy['exclusive'] = $this->InsertOnly; 96 | } 97 | if (!empty($this->DetectMime)) { 98 | $policy['detectMime'] = $this->DetectMime; 99 | } 100 | if (!empty($this->FsizeLimit)) { 101 | $policy['fsizeLimit'] = $this->FsizeLimit; 102 | } 103 | if (!empty($this->SaveKey)) { 104 | $policy['saveKey'] = $this->SaveKey; 105 | } 106 | if (!empty($this->PersistentOps)) { 107 | $policy['persistentOps'] = $this->PersistentOps; 108 | } 109 | if (!empty($this->PersistentPipeline)) { 110 | $policy['persistentPipeline'] = $this->PersistentPipeline; 111 | } 112 | if (!empty($this->PersistentNotifyUrl)) { 113 | $policy['persistentNotifyUrl'] = $this->PersistentNotifyUrl; 114 | } 115 | if (!empty($this->FopTimeout)) { 116 | $policy['fopTimeout'] = $this->FopTimeout; 117 | } 118 | if (!empty($this->MimeLimit)) { 119 | $policy['mimeLimit'] = $this->MimeLimit; 120 | } 121 | 122 | 123 | $b = json_encode($policy); 124 | return Qiniu_SignWithData($mac, $b); 125 | } 126 | } 127 | 128 | // ---------------------------------------------------------- 129 | // class Qiniu_RS_EntryPath 130 | 131 | class Qiniu_RS_EntryPath 132 | { 133 | public $bucket; 134 | public $key; 135 | 136 | public function __construct($bucket, $key) 137 | { 138 | $this->bucket = $bucket; 139 | $this->key = $key; 140 | } 141 | } 142 | 143 | // ---------------------------------------------------------- 144 | // class Qiniu_RS_EntryPathPair 145 | 146 | class Qiniu_RS_EntryPathPair 147 | { 148 | public $src; 149 | public $dest; 150 | 151 | public function __construct($src, $dest) 152 | { 153 | $this->src = $src; 154 | $this->dest = $dest; 155 | } 156 | } 157 | 158 | // ---------------------------------------------------------- 159 | 160 | function Qiniu_RS_URIStat($bucket, $key) 161 | { 162 | return '/stat/' . Qiniu_Encode("$bucket:$key"); 163 | } 164 | 165 | function Qiniu_RS_URIDelete($bucket, $key) 166 | { 167 | return '/delete/' . Qiniu_Encode("$bucket:$key"); 168 | } 169 | 170 | function Qiniu_RS_URICopy($bucketSrc, $keySrc, $bucketDest, $keyDest) 171 | { 172 | return '/copy/' . Qiniu_Encode("$bucketSrc:$keySrc") . '/' . Qiniu_Encode("$bucketDest:$keyDest"); 173 | } 174 | 175 | function Qiniu_RS_URIMove($bucketSrc, $keySrc, $bucketDest, $keyDest) 176 | { 177 | return '/move/' . Qiniu_Encode("$bucketSrc:$keySrc") . '/' . Qiniu_Encode("$bucketDest:$keyDest"); 178 | } 179 | 180 | // ---------------------------------------------------------- 181 | 182 | function Qiniu_RS_Stat($self, $bucket, $key) // => ($statRet, $error) 183 | { 184 | global $QINIU_RS_HOST; 185 | $uri = Qiniu_RS_URIStat($bucket, $key); 186 | return Qiniu_Client_Call($self, $QINIU_RS_HOST . $uri); 187 | } 188 | 189 | function Qiniu_RS_Delete($self, $bucket, $key) // => $error 190 | { 191 | global $QINIU_RS_HOST; 192 | $uri = Qiniu_RS_URIDelete($bucket, $key); 193 | return Qiniu_Client_CallNoRet($self, $QINIU_RS_HOST . $uri); 194 | } 195 | 196 | function Qiniu_RS_Move($self, $bucketSrc, $keySrc, $bucketDest, $keyDest) // => $error 197 | { 198 | global $QINIU_RS_HOST; 199 | $uri = Qiniu_RS_URIMove($bucketSrc, $keySrc, $bucketDest, $keyDest); 200 | return Qiniu_Client_CallNoRet($self, $QINIU_RS_HOST . $uri); 201 | } 202 | 203 | function Qiniu_RS_Copy($self, $bucketSrc, $keySrc, $bucketDest, $keyDest) // => $error 204 | { 205 | global $QINIU_RS_HOST; 206 | $uri = Qiniu_RS_URICopy($bucketSrc, $keySrc, $bucketDest, $keyDest); 207 | return Qiniu_Client_CallNoRet($self, $QINIU_RS_HOST . $uri); 208 | } 209 | 210 | // ---------------------------------------------------------- 211 | // batch 212 | 213 | function Qiniu_RS_Batch($self, $ops) // => ($data, $error) 214 | { 215 | global $QINIU_RS_HOST; 216 | $url = $QINIU_RS_HOST . '/batch'; 217 | $params = 'op=' . implode('&op=', $ops); 218 | return Qiniu_Client_CallWithForm($self, $url, $params); 219 | } 220 | 221 | function Qiniu_RS_BatchStat($self, $entryPaths) 222 | { 223 | $params = array(); 224 | foreach ($entryPaths as $entryPath) { 225 | $params[] = Qiniu_RS_URIStat($entryPath->bucket, $entryPath->key); 226 | } 227 | return Qiniu_RS_Batch($self,$params); 228 | } 229 | 230 | function Qiniu_RS_BatchDelete($self, $entryPaths) 231 | { 232 | $params = array(); 233 | foreach ($entryPaths as $entryPath) { 234 | $params[] = Qiniu_RS_URIDelete($entryPath->bucket, $entryPath->key); 235 | } 236 | return Qiniu_RS_Batch($self, $params); 237 | } 238 | 239 | function Qiniu_RS_BatchMove($self, $entryPairs) 240 | { 241 | $params = array(); 242 | foreach ($entryPairs as $entryPair) { 243 | $src = $entryPair->src; 244 | $dest = $entryPair->dest; 245 | $params[] = Qiniu_RS_URIMove($src->bucket, $src->key, $dest->bucket, $dest->key); 246 | } 247 | return Qiniu_RS_Batch($self, $params); 248 | } 249 | 250 | function Qiniu_RS_BatchCopy($self, $entryPairs) 251 | { 252 | $params = array(); 253 | foreach ($entryPairs as $entryPair) { 254 | $src = $entryPair->src; 255 | $dest = $entryPair->dest; 256 | $params[] = Qiniu_RS_URICopy($src->bucket, $src->key, $dest->bucket, $dest->key); 257 | } 258 | return Qiniu_RS_Batch($self, $params); 259 | } 260 | 261 | // ---------------------------------------------------------- 262 | 263 | -------------------------------------------------------------------------------- /sdk/rs_utils.php: -------------------------------------------------------------------------------- 1 | ($putRet, $err) 8 | { 9 | $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); 10 | $upToken = $putPolicy->Token($self->Mac); 11 | return Qiniu_Put($upToken, $key, $body, $putExtra); 12 | } 13 | 14 | function Qiniu_RS_PutFile($self, $bucket, $key, $localFile, $putExtra) // => ($putRet, $err) 15 | { 16 | $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); 17 | $upToken = $putPolicy->Token($self->Mac); 18 | return Qiniu_PutFile($upToken, $key, $localFile, $putExtra); 19 | } 20 | 21 | function Qiniu_RS_Rput($self, $bucket, $key, $body, $fsize, $putExtra) // => ($putRet, $err) 22 | { 23 | $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); 24 | $upToken = $putPolicy->Token($self->Mac); 25 | if ($putExtra == null) { 26 | $putExtra = new Qiniu_Rio_PutExtra($bucket); 27 | } else { 28 | $putExtra->Bucket = $bucket; 29 | } 30 | return Qiniu_Rio_Put($upToken, $key, $body, $fsize, $putExtra); 31 | } 32 | 33 | function Qiniu_RS_RputFile($self, $bucket, $key, $localFile, $putExtra) // => ($putRet, $err) 34 | { 35 | $putPolicy = new Qiniu_RS_PutPolicy("$bucket:$key"); 36 | $upToken = $putPolicy->Token($self->Mac); 37 | if ($putExtra == null) { 38 | $putExtra = new Qiniu_Rio_PutExtra($bucket); 39 | } else { 40 | $putExtra->Bucket = $bucket; 41 | } 42 | return Qiniu_Rio_PutFile($upToken, $key, $localFile, $putExtra); 43 | } 44 | 45 | -------------------------------------------------------------------------------- /sdk/rsf.php: -------------------------------------------------------------------------------- 1 | ($items, 14 | // $markerOut, $err) 15 | { 16 | global $QINIU_RSF_HOST; 17 | 18 | $query = array ( 19 | 'bucket' => $bucket 20 | ); 21 | if (! empty ( $prefix )) { 22 | $query ['prefix'] = $prefix; 23 | } 24 | if (! empty ( $marker )) { 25 | $query ['marker'] = $marker; 26 | } 27 | if (! empty ( $limit )) { 28 | $query ['limit'] = $limit; 29 | } 30 | 31 | $url = $QINIU_RSF_HOST . '/list?' . http_build_query ( $query ); 32 | list ( $ret, $err ) = Qiniu_Client_Call ( $self, $url ); 33 | if ($err !== null) { 34 | return array ( 35 | null, 36 | '', 37 | $err 38 | ); 39 | } 40 | 41 | $items = $ret ['items']; 42 | if (empty ( $ret ['marker'] )) { 43 | $markerOut = ''; 44 | $err = Qiniu_RSF_EOF; 45 | } else { 46 | $markerOut = $ret ['marker']; 47 | } 48 | return array ( 49 | $items, 50 | $markerOut, 51 | $err 52 | ); 53 | } 54 | 55 | /** 56 | * 从指定URL抓取资源,并将该资源存储到指定空间中 57 | * 58 | * @param $url 指定的URL 59 | * @param $bucket 目标资源空间 60 | * @param $key 目标资源文件名 61 | * 62 | * @return array[] 包含已拉取的文件信息。 63 | * 成功时: [ 64 | * [ 65 | * "hash" => "", 66 | * "key" => "" 67 | * ], 68 | * null 69 | * ] 70 | * 71 | * 失败时: [ 72 | * null, 73 | * Qiniu/Http/Error 74 | * ] 75 | * @link http://developer.qiniu.com/docs/v6/api/reference/rs/fetch.html 76 | */ 77 | function Qiniu_Fetch($self,$url, $bucket, $key) { 78 | global $QINIU_FETCH_HOST; 79 | $resource = base64_urlSafeEncode ( $url ); 80 | $to = entry ( $bucket, $key ); 81 | $url = $QINIU_FETCH_HOST . '/fetch/' . $resource . '/to/' . $to; 82 | return Qiniu_Client_Call ( $self, $url ); 83 | } 84 | 85 | /** 86 | * 计算七牛API中的数据格式 87 | * 88 | * @param $bucket 待操作的空间名 89 | * @param $key 待操作的文件名 90 | * 91 | * @return 符合七牛API规格的数据格式 92 | * @link http://developer.qiniu.com/docs/v6/api/reference/data-formats.html 93 | */ 94 | function entry($bucket, $key) { 95 | $en = $bucket; 96 | if (! empty ( $key )) { 97 | $en = $bucket . ':' . $key; 98 | } 99 | return base64_urlSafeEncode ( $en ); 100 | } 101 | 102 | 103 | /** 104 | * 对提供的数据进行urlsafe的base64编码。 105 | * 106 | * @param string $data 待编码的数据,一般为字符串 107 | * 108 | * @return string 编码后的字符串 109 | * @link http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64 110 | */ 111 | function base64_urlSafeEncode($data) 112 | { 113 | $find = array('+', '/'); 114 | $replace = array('-', '_'); 115 | return str_replace($find, $replace, base64_encode($data)); 116 | } -------------------------------------------------------------------------------- /sdk/utils.php: -------------------------------------------------------------------------------- 1 |