QiniuStorage.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Library for ThinkAdmin
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2014~2019 广州楚才信息科技有限公司 [ http://www.cuci.cc ]
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://demo.thinkadmin.top
  8. // +----------------------------------------------------------------------
  9. // | 开源协议 ( https://mit-license.org )
  10. // +----------------------------------------------------------------------
  11. // | gitee 仓库地址 :https://gitee.com/zoujingli/ThinkLibrary
  12. // | github 仓库地址 :https://github.com/zoujingli/ThinkLibrary
  13. // +----------------------------------------------------------------------
  14. namespace think\admin\storage;
  15. use think\admin\extend\HttpExtend;
  16. use think\admin\Storage;
  17. /**
  18. * 七牛云存储支持
  19. * Class QiniuStorage
  20. * @package think\admin\storage
  21. */
  22. class QiniuStorage extends Storage
  23. {
  24. private $bucket;
  25. private $domain;
  26. private $accessKey;
  27. private $secretKey;
  28. /**
  29. * 存储引擎初始化
  30. * @return $this
  31. * @throws \think\Exception
  32. * @throws \think\db\exception\DataNotFoundException
  33. * @throws \think\db\exception\DbException
  34. * @throws \think\db\exception\ModelNotFoundException
  35. */
  36. protected function initialize(): Storage
  37. {
  38. // 读取配置文件
  39. $this->bucket = sysconf('storage.qiniu_bucket');
  40. $this->domain = sysconf('storage.qiniu_http_domain');
  41. $this->accessKey = sysconf('storage.qiniu_access_key');
  42. $this->secretKey = sysconf('storage.qiniu_secret_key');
  43. // 计算链接前缀
  44. $type = strtolower(sysconf('storage.qiniu_http_protocol'));
  45. if ($type === 'auto') $this->prefix = "//{$this->domain}/";
  46. elseif ($type === 'http') $this->prefix = "http://{$this->domain}/";
  47. elseif ($type === 'https') $this->prefix = "https://{$this->domain}/";
  48. else throw new \think\Exception('未配置七牛云URL域名哦');
  49. return $this;
  50. }
  51. /**
  52. * 获取当前实例对象
  53. * @param null $name
  54. * @return static
  55. * @throws \think\Exception
  56. * @throws \think\db\exception\DataNotFoundException
  57. * @throws \think\db\exception\DbException
  58. * @throws \think\db\exception\ModelNotFoundException
  59. */
  60. public static function instance($name = null): Storage
  61. {
  62. return parent::instance('qiniu');
  63. }
  64. /**
  65. * 上传文件内容
  66. * @param string $name 文件名称
  67. * @param string $file 文件内容
  68. * @param boolean $safe 安全模式
  69. * @return array
  70. * @throws \think\Exception
  71. * @throws \think\db\exception\DataNotFoundException
  72. * @throws \think\db\exception\DbException
  73. * @throws \think\db\exception\ModelNotFoundException
  74. */
  75. public function set($name, $file, $safe = false)
  76. {
  77. $token = $this->buildUploadToken($name);
  78. list($attrs, $frontier) = [[], uniqid()];
  79. foreach (['key' => $name, 'token' => $token, 'fileName' => $name] as $key => $value) {
  80. $attrs[] = "--{$frontier}";
  81. $attrs[] = "Content-Disposition:form-data; name=\"{$key}\"";
  82. $attrs[] = "";
  83. $attrs[] = $value;
  84. }
  85. $attrs[] = "--{$frontier}";
  86. $attrs[] = "Content-Disposition:form-data; name=\"file\"; filename=\"{$name}\"";
  87. $attrs[] = "";
  88. $attrs[] = $file;
  89. $attrs[] = "--{$frontier}--";
  90. return json_decode(HttpExtend::post($this->upload(), join("\r\n", $attrs), [
  91. 'headers' => ["Content-type:multipart/form-data;boundary={$frontier}"],
  92. ]), true);
  93. }
  94. /**
  95. * 根据文件名读取文件内容
  96. * @param string $name 文件名称
  97. * @param boolean $safe 安全模式
  98. * @return string
  99. */
  100. public function get($name, $safe = false)
  101. {
  102. $url = $this->url($name, $safe) . "?e=" . time();
  103. $token = "{$this->accessKey}:{$this->safeBase64(hash_hmac('sha1', $url, $this->secretKey, true))}";
  104. return file_get_contents("{$url}&token={$token}");
  105. }
  106. /**
  107. * 删除存储的文件
  108. * @param string $name 文件名称
  109. * @param boolean $safe 安全模式
  110. * @return boolean|null
  111. */
  112. public function del($name, $safe = false)
  113. {
  114. list($EncodedEntryURI, $AccessToken) = $this->getAccessToken($name, 'delete');
  115. $data = json_decode(HttpExtend::post("http://rs.qiniu.com/delete/{$EncodedEntryURI}", [], [
  116. 'headers' => ["Authorization:QBox {$AccessToken}"],
  117. ]), true);
  118. return empty($data['error']);
  119. }
  120. /**
  121. * 检查文件是否已经存在
  122. * @param string $name 文件名称
  123. * @param boolean $safe 安全模式
  124. * @return boolean
  125. */
  126. public function has($name, $safe = false)
  127. {
  128. return is_array($this->info($name, $safe));
  129. }
  130. /**
  131. * 获取文件当前URL地址
  132. * @param string $name 文件名称
  133. * @param boolean $safe 安全模式
  134. * @return string
  135. */
  136. public function url($name, $safe = false)
  137. {
  138. return "{$this->prefix}{$name}";
  139. }
  140. /**
  141. * 获取文件存储路径
  142. * @param string $name 文件名称
  143. * @param boolean $safe 安全模式
  144. * @return string
  145. */
  146. public function path($name, $safe = false)
  147. {
  148. return $this->url($name, $safe);
  149. }
  150. /**
  151. * 获取文件存储信息
  152. * @param string $name 文件名称
  153. * @param boolean $safe 安全模式
  154. * @return array
  155. */
  156. public function info($name, $safe = false)
  157. {
  158. list($entry, $token) = $this->getAccessToken($name);
  159. $data = json_decode(HttpExtend::get("http://rs.qiniu.com/stat/{$entry}", [], ['headers' => ["Authorization: QBox {$token}"]]), true);
  160. return isset($data['md5']) ? ['file' => $name, 'url' => $this->url($name, $safe), 'hash' => $data['md5'], 'key' => $name] : [];
  161. }
  162. /**
  163. * 获取文件上传地址
  164. * @return string
  165. * @throws \think\Exception
  166. * @throws \think\db\exception\DataNotFoundException
  167. * @throws \think\db\exception\DbException
  168. * @throws \think\db\exception\ModelNotFoundException
  169. */
  170. public function upload()
  171. {
  172. $protocol = $this->app->request->isSsl() ? 'https' : 'http';
  173. switch (sysconf('storage.qiniu_region')) {
  174. case '华东':
  175. return "{$protocol}://up.qiniup.com";
  176. case '华北':
  177. return "{$protocol}://up-z1.qiniup.com";
  178. case '华南':
  179. return "{$protocol}://up-z2.qiniup.com";
  180. case '北美':
  181. return "{$protocol}://up-na0.qiniup.com";
  182. case '东南亚':
  183. return "{$protocol}://up-as0.qiniup.com";
  184. default:
  185. throw new \think\Exception('未配置七牛云空间区域哦');
  186. }
  187. }
  188. /**
  189. * 获取文件上传令牌
  190. * @param string $name 文件名称
  191. * @param integer $expires 有效时间
  192. * @return string
  193. */
  194. public function buildUploadToken($name = null, $expires = 3600)
  195. {
  196. $policy = $this->safeBase64(json_encode([
  197. "deadline" => time() + $expires, "scope" => is_null($name) ? $this->bucket : "{$this->bucket}:{$name}",
  198. 'returnBody' => json_encode(['uploaded' => true, 'filename' => '$(key)', 'url' => "{$this->prefix}$(key)"], JSON_UNESCAPED_UNICODE),
  199. ]));
  200. return "{$this->accessKey}:{$this->safeBase64(hash_hmac('sha1', $policy, $this->secretKey, true))}:{$policy}";
  201. }
  202. /**
  203. * URL安全的Base64编码
  204. * @param string $content
  205. * @return string
  206. */
  207. private function safeBase64($content)
  208. {
  209. return str_replace(['+', '/'], ['-', '_'], base64_encode($content));
  210. }
  211. /**
  212. * 获取对象管理凭证
  213. * @param string $name 文件名称
  214. * @param string $type 操作类型
  215. * @return array
  216. */
  217. private function getAccessToken($name, $type = 'stat')
  218. {
  219. $entry = $this->safeBase64("{$this->bucket}:{$name}");
  220. $sign = hash_hmac('sha1', "/{$type}/{$entry}\n", $this->secretKey, true);
  221. return [$entry, "{$this->accessKey}:{$this->safeBase64($sign)}"];
  222. }
  223. }