├── README.md
├── composer.json
├── lib
└── Qiniu
│ ├── Client.php
│ ├── Mac.php
│ ├── Qiniu.php
│ ├── Request.php
│ ├── Response.php
│ ├── Result.php
│ └── Util.php
├── phpunit.xml
└── test
├── MacTest.php
├── RequestTest.php
└── UtilTest.php
/README.md:
--------------------------------------------------------------------------------
1 | # php-qiniu
2 |
3 | [七牛云存储](http://qiniu.com)非官方SDK,采用[PSR规范](https://github.com/hfcorriez/fig-standards),支持[Composer](http://getcomposer.org)安装
4 |
5 | `开发中...`
6 |
7 | # 安装
8 |
9 | 添加 `"qiniu/qiniu": "*"` 到 [`composer.json`](http://getcomposer.org).
10 |
11 | ```
12 | composer.phar install
13 | ```
14 |
15 | # 引导
16 |
17 | - [基本用法](#基本用法)
18 | - [上传](#上传)
19 | - [上传文件](#上传文件)
20 | - [上传字符串](#上传字符串)
21 | - [基本操作](#基本操作)
22 | - [查看文件](#查看文件)
23 | - [复制文件](#复制文件)
24 | - [移动文件](#移动文件)
25 | - [删除文件](#删除文件)
26 | - [筛选文件](#筛选文件)
27 | - [批量操作](#批量操作)
28 | - [图片查看](#图片查看)
29 | - [查看图片信息](#查看图片信息)
30 | - [查看图片Exif](#查看图片exif)
31 | - [图片生成](#图片生成)
32 | - [缩略图生成](#缩略图生成)
33 | - [高级图片处理](#高级图片处理)
34 |
35 | # 使用
36 |
37 | ## 基本用法
38 |
39 | ```php
40 | require dirname(__DIR__) . '/vendor/autoload.php';
41 |
42 | $client = \Qiniu\Qiniu::create(array(
43 | 'access_key' => '',
44 | 'secret_key' => '',
45 | 'bucket' => ''
46 | ));
47 |
48 | // 查看文件状态
49 | $res = $client->stat('jobs.jpg');
50 |
51 | print_r($res);
52 | ```
53 |
54 | 输出
55 |
56 | ```
57 | Qiniu\Result Object
58 | (
59 | [error] =>
60 | [data] => Array
61 | (
62 | [fsize] => 2425435
63 | [hash] => Fqng6_0hUF8Q-POxmWNi8JD9VRmy
64 | [mimeType] => image/jpeg
65 | [putTime] => 13707100228731119
66 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
67 | )
68 | [debug] => Array
69 | (
70 | [log] => BDT;BDT;LBD
71 | [id] => zxwAAMj4OP6_7yET
72 | )
73 | )
74 | ```
75 |
76 | 推荐用法
77 |
78 | ```php
79 | if ($res->ok()) {
80 | // 成功上传或者操作
81 |
82 | // 获取返回的数据
83 | $data = $res->data; // Or $res->toArray()
84 |
85 | //做一些事情
86 | } else {
87 | // 失败,获取失败信息
88 | $res->error;
89 |
90 | // 七牛的Debug头信息
91 | $res->debug;
92 | }
93 | ```
94 |
95 | ## 上传
96 |
97 | ### 上传文件
98 |
99 | ```php
100 | $res = $client->uploadFile('/home/hfcorriez/Code/jobs.jpg', 'jobs.jpg');
101 |
102 | /*
103 | $res->data:
104 |
105 | Array
106 | (
107 | [key] => 4276
108 | [hash] => FpLJ7yTujtJ85yU6QLA79ZaR3kKg
109 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
110 | )
111 | */
112 | ```
113 |
114 | ### 上传字符串
115 |
116 | ```php
117 | $res = $client->upload('I am Qiniu SDK', 'readme.txt');
118 |
119 | /*
120 | $res->data:
121 |
122 | Array
123 | (
124 | [key] => 4276
125 | [hash] => FpLJ7yTujtJ85yU6QLA79ZaR3kKg
126 | [url] => http://php-sdk.qiniudn.com/readme.txt
127 | )
128 | */
129 | ```
130 |
131 | ## 文件操作
132 |
133 | ### 查看文件
134 |
135 | ```php
136 | // 查看文件状态
137 | $res = $client->stat('jobs.jpg');
138 |
139 | /*
140 | $res->data:
141 |
142 | [data] => Array
143 | (
144 | [fsize] => 2425435
145 | [hash] => Fqng6_0hUF8Q-POxmWNi8JD9VRmy
146 | [mimeType] => image/jpeg
147 | [putTime] => 13787406554413228
148 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
149 | )
150 | */
151 | ```
152 |
153 | ### 复制文件
154 |
155 | ```php
156 | $res = $client->copy('jobs.jpg', 'jobs.jpg.new');
157 |
158 | /*
159 | $res->data:
160 |
161 | [data] => Array
162 | (
163 | [url] => http://php-sdk.qiniudn.com/jobs.jpg.new
164 | )
165 | */
166 | ```
167 |
168 | ### 移动文件
169 |
170 | ```php
171 | $res = $client->move('jobs.jpg.new', 'jobs.jpg');
172 |
173 | /*
174 | $res->data:
175 |
176 | [data] => Array
177 | (
178 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
179 | )
180 | */
181 | ```
182 |
183 | ### 删除文件
184 |
185 | ```php
186 | $res = $client->delete('jobs.jpg');
187 |
188 | /*
189 | $res->data:
190 |
191 | [data] => Array
192 | (
193 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
194 | )
195 | */
196 | ```
197 |
198 | ### 筛选文件
199 |
200 | ```php
201 | $res = $client->ls();
202 |
203 | /*
204 | $res->data:
205 |
206 | Array
207 | (
208 | [items] => Array
209 | (
210 | [0] => Array
211 | (
212 | [fsize] => 2425435
213 | [putTime] => 13787406554413228
214 | [key] => jobs.jpg
215 | [hash] => Fqng6_0hUF8Q-POxmWNi8JD9VRmy
216 | [mimeType] => image/jpeg
217 | )
218 | )
219 | )
220 | */
221 | ```
222 |
223 | ## 批量操作
224 |
225 | ```php
226 | $res = $client->batch()
227 | ->stat('jobs.jpg')
228 | ->copy('jobs.jpg', 'jobs.jpg.new')
229 | ->move('jobs.jpg.new', 'jobs.jpg.old')
230 | ->delete('jobs.jpg.old')
231 | ->exec();
232 |
233 | /*
234 | $res:
235 |
236 | Qiniu\Result Object
237 | (
238 | [error] =>
239 | [data] => Array
240 | (
241 | [0] => Qiniu\Result Object
242 | (
243 | [error] =>
244 | [data] => Array
245 | (
246 | [fsize] => 2425435
247 | [hash] => Fqng6_0hUF8Q-POxmWNi8JD9VRmy
248 | [mimeType] => image/jpeg
249 | [putTime] => 13787430682676023
250 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
251 | )
252 |
253 | [response] =>
254 | [debug] => Array
255 | (
256 | [log] => MQ;MC/404;RS.mcd
257 | [id] => u00AAFaxXKXYfiIT
258 | )
259 |
260 | )
261 |
262 | [1] => Qiniu\Result Object
263 | (
264 | [error] =>
265 | [data] => Array
266 | (
267 | [url] => http://php-sdk.qiniudn.com/jobs.jpg
268 | )
269 |
270 | [response] =>
271 | [debug] => Array
272 | (
273 | [log] => MQ;MC/404;RS.mcd
274 | [id] => u00AAFaxXKXYfiIT
275 | )
276 |
277 | )
278 |
279 | [2] => Qiniu\Result Object
280 | (
281 | [error] =>
282 | [data] => Array
283 | (
284 | [url] => http://php-sdk.qiniudn.com/jobs.jpg.new
285 | )
286 |
287 | [response] =>
288 | [debug] => Array
289 | (
290 | [log] => MQ;MC/404;RS.mcd
291 | [id] => u00AAFaxXKXYfiIT
292 | )
293 |
294 | )
295 |
296 | [3] => Qiniu\Result Object
297 | (
298 | [error] =>
299 | [data] => Array
300 | (
301 | [url] => http://php-sdk.qiniudn.com/jobs.jpg.old
302 | )
303 |
304 | [response] =>
305 | [debug] => Array
306 | (
307 | [log] => MQ;MC/404;RS.mcd
308 | [id] => u00AAFaxXKXYfiIT
309 | )
310 |
311 | )
312 |
313 | )
314 | ...
315 | )
316 | */
317 | ```
318 |
319 | ## 图片查看
320 |
321 | ### 查看图片信息
322 |
323 | 查看图片的信息,信息参数介绍参见[七牛官方文档](http://docs.qiniu.com/api/v6/image-process.html#imageInfo)
324 |
325 | ```php
326 | $res = $client->imageInfo('qn.jpeg');
327 |
328 | /*
329 | $res->data:
330 |
331 | Array
332 | (
333 | [format] => jpeg
334 | [width] => 640
335 | [height] => 423
336 | [colorModel] => ycbcr
337 | )
338 | */
339 | ```
340 |
341 | ### 查看图片Exif
342 |
343 | EXIF (Exchangeable image file format),是专门为数码相机的照片设定的可交换图像文件格式。详情参见[EXIF](http://zh.wikipedia.org/wiki/EXIF)
344 |
345 | ```php
346 | $res = $client->exif('qn.jpeg');
347 |
348 | /*
349 | $res->data:
350 |
351 | Array
352 | (
353 | [ApertureValue] => Array
354 | (
355 | [val] => 2.53 EV (f/2.4)
356 | [type] => 5
357 | )
358 |
359 | [BrightnessValue] => Array
360 | (
361 | [val] => 5.31 EV (135.85 cd/m^2)
362 | [type] => 10
363 | )
364 | ...
365 | )
366 | */
367 | ```
368 |
369 | ## 图片生成
370 |
371 | ### 缩略图生成
372 |
373 | 参数列表请参照[七牛官方文档](http://docs.qiniu.com/api/v6/image-process.html#imageMogr)
374 |
375 | ```php
376 | $url = $client->imageView('jobs.jpg', array("mode" => 1, "width" => 100, "height" => 100, "q" => 50, "format" => 'png'));
377 |
378 | /*
379 | $url:
380 |
381 | http://php-sdk.qiniudn.com/jobs.jpg?imageView/1/w/100/h/100/q/50/format/png
382 | */
383 | ```
384 |
385 | ### 高级图片处理
386 |
387 | 包括(缩略、裁剪、旋转、转化),参数列表请参照[七牛官方文档](http://docs.qiniu.com/api/v6/image-process.html#imageMogr)
388 |
389 | ```php
390 | $res = $client->imageMogr('jobs.jpg', array(
391 | "thumbnail" => '!50p',
392 | "gravity" => 'NorthWest',
393 | "quality" => 50,
394 | "rotate" => -50,
395 | "format" => 'gif'
396 | ));
397 |
398 | /*
399 | $url:
400 |
401 | http://php-sdk.qiniudn.com/jobs.jpg?imageMogr/v2/auto-orient/thumbnail/!50p/gravity/NorthWest/quality/50/rotate/-50/format/gif
402 | */
403 | ```
404 |
405 | # License
406 |
407 | (The MIT License)
408 |
409 | Copyright (c) 2012 hfcorriez <hfcorriez@gmail.com>
410 |
411 | Permission is hereby granted, free of charge, to any person obtaining
412 | a copy of this software and associated documentation files (the
413 | 'Software'), to deal in the Software without restriction, including
414 | without limitation the rights to use, copy, modify, merge, publish,
415 | distribute, sublicense, and/or sell copies of the Software, and to
416 | permit persons to whom the Software is furnished to do so, subject to
417 | the following conditions:
418 |
419 | The above copyright notice and this permission notice shall be
420 | included in all copies or substantial portions of the Software.
421 |
422 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
423 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
424 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
425 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
426 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
427 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
428 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qiniu/qiniu",
3 | "description": "Qiniu SDK",
4 | "keywords": ["sdk", "cloud storage", "api", "qiniu"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Corrie Zhao",
9 | "email": "hfcorriez@gmail.com"
10 | }
11 | ],
12 | "require": {
13 | "php": ">=5.3.9"
14 | },
15 | "autoload": {
16 | "psr-0": {
17 | "Qiniu": "lib/"
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/lib/Qiniu/Client.php:
--------------------------------------------------------------------------------
1 | null,
13 | 'secret_key' => null,
14 | 'bucket' => null,
15 | 'domain' => null,
16 | 'timeout' => '3600',
17 | 'upload_url' => 'http://up.qiniu.com',
18 | 'rs_url' => 'http://rs.qbox.me',
19 | 'rsf_url' => 'http://rsf.qbox.me',
20 | 'base_url' => null
21 | );
22 |
23 | /**
24 | * @var array Image url options
25 | */
26 | protected $image_options = array(
27 | 'imageView' => array(
28 | 'mode' => array('type' => 'no-key'),
29 | 'w' => array('alias' => 'width'),
30 | 'h' => array('alias' => 'height'),
31 | 'q' => array('alias' => 'quality'),
32 | 'format' => array('alias' => 'f')
33 | ),
34 | 'imageMogr' => array(
35 | '_path' => 'v2',
36 | 'auto-orient' => array('type' => 'no-value', 'default' => true),
37 | 'thumbnail' => array('alias' => array('thumb', 't')),
38 | 'gravity' => array('alias' => 'g'),
39 | 'crop' => array('alias' => 'c'),
40 | 'quality' => array('alias' => 'q'),
41 | 'rotate' => array('alias' => 'r'),
42 | 'format' => array('alias' => 'f')
43 | )
44 | );
45 |
46 | protected $batch = false;
47 | protected $batch_operators = array();
48 |
49 | /**
50 | * @var Mac
51 | */
52 | protected $mac;
53 |
54 | public function __construct(array $options)
55 | {
56 | $this->options = $options + $this->options;
57 |
58 | if (!$this->options['access_key'] || !$this->options['secret_key'] || !$this->options['bucket']) {
59 | throw new \InvalidArgumentException("Options access_key, secret_key and bucket required");
60 | }
61 |
62 | $this->mac = new Mac($this->options['access_key'], $this->options['secret_key']);
63 |
64 | if (!$this->options['base_url']) $this->options['base_url'] = $this->options['domain'] ? $this->options['domain'] : ('http://' . $this->options['bucket'] . '.qiniudn.com');
65 | }
66 |
67 | /**
68 | * Upload file
69 | *
70 | * @param string $path
71 | * @param string $key
72 | * @throws \InvalidArgumentException
73 | * @return bool|Result
74 | */
75 | public function uploadFile($path, $key)
76 | {
77 | if (!is_file($path)) throw new \InvalidArgumentException("Can not upload non-exists file: $path");
78 | return $this->uploadRequest(file_get_contents($path), $key);
79 | }
80 |
81 | /**
82 | * Upload string
83 | *
84 | * @param string $string
85 | * @param string $key
86 | * @return bool|Result
87 | */
88 | public function upload($string, $key)
89 | {
90 | return $this->uploadRequest($string, $key);
91 | }
92 |
93 | /**
94 | * Get file stats
95 | *
96 | * @param string $key
97 | * @return bool|Result|Client
98 | */
99 | public function stat($key)
100 | {
101 | $uri = '/stat/' . Util::uriEncode("{$this->options['bucket']}:$key");
102 |
103 | if ($this->batch) {
104 | $this->batch_operators[] = array($uri, $key);
105 | return $this;
106 | } else {
107 | return $this->operateRequest($uri, $key);
108 | }
109 | }
110 |
111 | /**
112 | * Move file
113 | *
114 | * @param string $key
115 | * @param string $new_key
116 | * @return bool|Result|Client
117 | */
118 | public function move($key, $new_key)
119 | {
120 | $uri = '/move/' . Util::uriEncode("{$this->options['bucket']}:$key") . '/' . Util::uriEncode("{$this->options['bucket']}:$new_key");
121 |
122 | if ($this->batch) {
123 | $this->batch_operators[] = array($uri, $key);
124 | return $this;
125 | } else {
126 | return $this->operateRequest($uri, $new_key);
127 | }
128 | }
129 |
130 | /**
131 | * Copy file
132 | *
133 | * @param string $key
134 | * @param string $new_key
135 | * @return bool|Result|Client
136 | */
137 | public function copy($key, $new_key)
138 | {
139 | $uri = '/copy/' . Util::uriEncode("{$this->options['bucket']}:$key") . '/' . Util::uriEncode("{$this->options['bucket']}:$new_key");
140 |
141 | if ($this->batch) {
142 | $this->batch_operators[] = array($uri, $key);
143 | return $this;
144 | } else {
145 | return $this->operateRequest($uri, $new_key);
146 | }
147 | }
148 |
149 | /**
150 | * Delete file
151 | *
152 | * @param string $key
153 | * @return bool|Result|Client
154 | */
155 | public function delete($key)
156 | {
157 | $uri = '/delete/' . Util::uriEncode("{$this->options['bucket']}:$key");
158 |
159 | if ($this->batch) {
160 | $this->batch_operators[] = array($uri, $key);
161 | return $this;
162 | } else {
163 | return $this->operateRequest($uri, $key);
164 | }
165 | }
166 |
167 | /**
168 | *
169 | * @param $prefix
170 | * @return bool|Result
171 | */
172 | public function ls($prefix = '')
173 | {
174 | $query = array('bucket' => $this->options['bucket']) + (is_array($prefix) ? $prefix : array('prefix' => $prefix));
175 | $uri = '/list?' . http_build_query($query);
176 | return $this->operateRequest($uri, null, $this->options['rsf_url']);
177 | }
178 |
179 | /**
180 | * Start batch mode
181 | *
182 | * @return $this
183 | */
184 | public function batch()
185 | {
186 | $this->batch = true;
187 | return $this;
188 | }
189 |
190 | /**
191 | * Exec the batch
192 | *
193 | * @return Result
194 | * @throws \RuntimeException
195 | */
196 | public function exec()
197 | {
198 | if (!$this->batch) throw new \RuntimeException("Not in batch mode!");
199 |
200 | $ops = array();
201 | foreach ($this->batch_operators as $operator) {
202 | $ops[] = 'op=' . $operator[0];
203 | }
204 |
205 | $url = $this->options['rs_url'] . '/batch';
206 |
207 | $request = Request::create(array(
208 | 'url' => $url,
209 | 'body' => join('&', $ops),
210 | 'method' => 'POST'
211 | ));
212 | $token = $this->mac->signRequest('/batch', join('&', $ops));
213 | $request->header('authorization', 'QBox ' . $token);
214 |
215 | $result = new Result($response = $request->send(), $request);
216 | if (!$result->ok()) {
217 | return $result;
218 | }
219 |
220 | if (is_array($result->data)) {
221 | foreach ($result->data as $i => $item) {
222 | $r = new Result($response, $request);
223 | $r->data = isset($item['data']) ? $item['data'] : array();
224 | if ($item['code'] !== 200) {
225 | $r->error = $item['error'];
226 | }
227 | $r->response = null;
228 | $r->data['url'] = $this->options['base_url'] . '/' . $this->batch_operators[$i][1];
229 | $result->data[$i] = $r;
230 | }
231 | }
232 |
233 | // Reset batch mode
234 | $this->batch = false;
235 | $this->batch_operators = array();
236 |
237 | return $result;
238 | }
239 |
240 | /**
241 | * Get image info
242 | *
243 | * @param string $key
244 | * @return Result
245 | */
246 | public function imageInfo($key)
247 | {
248 | return $this->imageRequest($key, 'imageInfo');
249 | }
250 |
251 | /**
252 | * Get exif info
253 | *
254 | * @param string $key
255 | * @return Result
256 | */
257 | public function exif($key)
258 | {
259 | return $this->imageRequest($key, 'exif');
260 | }
261 |
262 | /**
263 | * Generate imageView url
264 | *
265 | * @param string $key
266 | * @param array $options
267 | * @return Result
268 | */
269 | public function imageView($key, array $options)
270 | {
271 | return $this->imageUrl($key, 'imageView', $options);
272 | }
273 |
274 | /**
275 | * Generate imageMogr url
276 | *
277 | * @param string $key
278 | * @param array $options
279 | * @return string
280 | */
281 | public function imageMogr($key, array $options)
282 | {
283 | return $this->imageUrl($key, 'imageMogr', $options);
284 | }
285 |
286 | /**
287 | * Generate image url
288 | *
289 | * @param string $key
290 | * @param string $type
291 | * @param array $options
292 | * @return string
293 | */
294 | protected function imageUrl($key, $type, array $options)
295 | {
296 | $paths = array($type);
297 | foreach ($this->image_options[$type] as $field => $opt) {
298 | if ($field == '_path') {
299 | $paths[] = $opt;
300 | continue;
301 | }
302 |
303 | $look = array($field);
304 | $value = null;
305 | $type = isset($opt['type']) ? $opt['type'] : '';
306 |
307 | if (isset($opt['alias'])) {
308 | $look = array_merge($look, (array)$opt['alias']);
309 | }
310 |
311 | foreach ($look as $f) {
312 | if (isset($options[$f])) {
313 | $value = $options[$f];
314 | break;
315 | }
316 | }
317 |
318 | if (!$value && isset($opt['default'])) $value = $opt['default'];
319 |
320 | if ($value !== null) {
321 | switch ($type) {
322 | case 'no-key':
323 | $paths[] = $value;
324 | break;
325 | case 'no-value':
326 | if ($value) $paths[] = $field;
327 | break;
328 | default:
329 | $paths[] = $field;
330 | $paths[] = $value;
331 | }
332 | }
333 | }
334 | return $this->options['base_url'] . '/' . $key . '?' . join('/', $paths);
335 | }
336 |
337 | /**
338 | * Image info request
339 | *
340 | * @param string $key
341 | * @param string $type
342 | * @return Result
343 | */
344 | protected function imageRequest($key, $type)
345 | {
346 | $url = $this->options['base_url'] . '/' . $key . '?' . $type;
347 | $request = Request::create($url);
348 | return new Result($request->get(), $request);
349 | }
350 |
351 | /**
352 | * Operate request
353 | *
354 | * @param string $uri
355 | * @param string $key
356 | * @param string $host
357 | * @return bool|Result
358 | */
359 | protected function operateRequest($uri, $key, $host = null)
360 | {
361 | $url = ($host ? $host : $this->options['rs_url']) . $uri;
362 | $request = Request::create(array(
363 | 'url' => $url,
364 | 'method' => 'POST',
365 | 'content-type' => 'application/x-www-form-urlencoded'
366 | ));
367 | $token = $this->mac->signRequest($uri);
368 | $request->header('authorization', 'QBox ' . $token);
369 | $result = new Result($request->send(), $request);
370 | if ($result->ok() && $key) {
371 | $result->data['url'] = $this->options['base_url'] . '/' . $key;
372 | }
373 | return $result;
374 | }
375 |
376 | /**
377 | * Upload request
378 | *
379 | * @param string $body
380 | * @param string|array $key
381 | * @param array $policy
382 | * @return bool|Result
383 | */
384 | public function uploadRequest($body, $key, $policy = null)
385 | {
386 | $options = (is_string($key) ? array('key' => $key) : array()) + array(
387 | 'filename' => null
388 | );
389 |
390 | $policy = (array)$policy + array(
391 | 'scope' => $this->options['bucket'],
392 | 'deadline' => time() + 3600,
393 | 'callbackUrl' => null,
394 | 'callbackBody' => null,
395 | 'returnUrl' => null,
396 | 'returnBody' => null,
397 | 'asyncOps' => null,
398 | 'endUser' => null
399 | );
400 |
401 | foreach ($policy as $k => $v) {
402 | if ($v === null) unset($policy[$k]);
403 | }
404 |
405 | $token = $this->mac->signWithData(json_encode($policy));
406 | $request = Request::create(array(
407 | 'url' => $this->options['upload_url'],
408 | 'method' => 'POST',
409 | 'headers' => array(
410 | 'content-type' => 'multipart/form-data'
411 | ),
412 | 'form' => array(
413 | 'token' => $token,
414 | 'key' => $options['key']
415 | )
416 | ))->file($body, basename($options['filename'] ? $options['filename'] : $options['key']));
417 | $result = new Result($request->send(), $request);
418 | if ($result->ok()) {
419 | $result->data['url'] = $this->options['base_url'] . '/' . $result->data['key'];
420 | }
421 | return $result;
422 | }
423 | }
--------------------------------------------------------------------------------
/lib/Qiniu/Mac.php:
--------------------------------------------------------------------------------
1 | access_key = $access_key;
13 | $this->secret_key = $secret_key;
14 | }
15 |
16 | /**
17 | * Sign data
18 | *
19 | * @param string $data
20 | * @return string
21 | */
22 | public function sign($data)
23 | {
24 | $sign = hash_hmac('sha1', $data, $this->secret_key, true);
25 | return $this->access_key . ':' . Util::uriEncode($sign);
26 | }
27 |
28 | /**
29 | * Sign with data
30 | *
31 | * @param string $data
32 | * @return string
33 | */
34 | public function signWithData($data)
35 | {
36 | $data = Util::uriEncode($data);
37 | return $this->sign($data) . ':' . $data;
38 | }
39 |
40 | /**
41 | * Sign url and body for generate token
42 | *
43 | * @param string $url
44 | * @param string $body
45 | * @return string
46 | */
47 | public function signRequest($url, $body = '')
48 | {
49 | $url = parse_url($url);
50 | $data = '';
51 | if (isset($url['path'])) {
52 | $data = $url['path'];
53 | }
54 | if (isset($url['query'])) {
55 | $data .= '?' . $url['query'];
56 | }
57 | $data .= "\n";
58 |
59 | if ($body) {
60 | $data .= $body;
61 | }
62 | return $this->sign($data);
63 | }
64 | }
--------------------------------------------------------------------------------
/lib/Qiniu/Qiniu.php:
--------------------------------------------------------------------------------
1 | 'GET',
12 | 'url' => null,
13 | 'headers' => array(),
14 | 'body' => '',
15 | 'form' => array(),
16 | 'files' => array(),
17 | 'encoding' => 'utf-8',
18 | 'timeout' => 60
19 | );
20 |
21 | /**
22 | * @var Response
23 | */
24 | public $response;
25 |
26 | /**
27 | * @var string
28 | */
29 | public $error;
30 |
31 | /**
32 | * Create request
33 | *
34 | * @param array $options
35 | * @return Request
36 | */
37 | public static function create($options = null)
38 | {
39 | return new self($options);
40 | }
41 |
42 | /**
43 | * @param array $options
44 | */
45 | public function __construct($options = null)
46 | {
47 | if (is_string($options)) {
48 | $options = array('url' => $options);
49 | }
50 | $this->options = (array)$options + $this->options;
51 | }
52 |
53 | /**
54 | * Set url
55 | *
56 | * @param string $url
57 | * @return Request
58 | */
59 | public function url($url = null)
60 | {
61 | if ($url) {
62 | $this->options['url'] = $url;
63 | return $this;
64 | } else {
65 | return $this->options['url'];
66 | }
67 | }
68 |
69 | /**
70 | * Set headers
71 | *
72 | * @param string|array $key
73 | * @param string $value
74 | * @return Request
75 | */
76 | public function header($key, $value = null)
77 | {
78 | if (is_array($key)) {
79 | foreach ($key as $k => $v) {
80 | $this->header($k, $v);
81 | }
82 | } else {
83 | $this->options['headers'][strtolower($key)] = $value;
84 | }
85 | return $this;
86 | }
87 |
88 | /**
89 | * @param string|array $key
90 | * @param string $value
91 | * @return Request
92 | */
93 | public function form($key, $value = null)
94 | {
95 | if (is_array($key)) {
96 | foreach ($key as $k => $v) {
97 | $this->form($k, $v);
98 | }
99 | } else {
100 | $this->options['form'][$key] = $value;
101 | }
102 | return $this;
103 | }
104 |
105 | /**
106 | * Alias form
107 | *
108 | * @param string|array $key
109 | * @param string $value
110 | * @return Request
111 | */
112 | public function data($key, $value = null)
113 | {
114 | return $this->form($key, $value);
115 | }
116 |
117 | /**
118 | * Attach file
119 | *
120 | * @param string $file
121 | * @param string $filename
122 | * @param string $name
123 | * @throws \InvalidArgumentException
124 | * @throws \Exception
125 | * @return Request
126 | */
127 | public function file($file, $filename = null, $name = 'file')
128 | {
129 | if (is_array($file)) {
130 | foreach ($file as $f) {
131 | $this->file($f[0], isset($f[1]) ? $f[1] : null, isset($f[2]) ? $f[2] : 'file');
132 | }
133 | } else {
134 | if ($filename === null) {
135 | if (!is_file($file)) throw new \InvalidArgumentException("Can not upload non-exists file: $file");
136 |
137 | if (($content = file_get_contents($file)) === false) throw new \Exception("Can not read file: $file");
138 | } else {
139 | $content = $file;
140 | }
141 | $this->options['files'][$name] = array(basename($filename), $content);
142 | }
143 | return $this;
144 | }
145 |
146 | /**
147 | * Get request
148 | *
149 | * @param string|array $url
150 | * @param array $data
151 | * @return Response
152 | */
153 | public function get($url = null, $data = null)
154 | {
155 | return $this->send($url, $data, 'GET');
156 | }
157 |
158 | /**
159 | * Post request
160 | *
161 | * @param string|array $url
162 | * @param array $data
163 | * @return Response
164 | */
165 | public function post($url = null, $data = null)
166 | {
167 | return $this->send($url, $data, 'POST');
168 | }
169 |
170 | /**
171 | * Upload files
172 | *
173 | * @param string|array $file
174 | * @param array $options
175 | * @param string $name
176 | * @return Response|bool
177 | */
178 | public function upload($file, $name = 'file', $options = null)
179 | {
180 | if (is_array($name)) {
181 | $name = $options ? $options : 'file';
182 | $options = $name;
183 | }
184 | $this->file($file, $name);
185 | return $this->send($options, null, 'POST');
186 | }
187 |
188 | /**
189 | * Put request
190 | *
191 | * @param string|array $url
192 | * @param array $data
193 | * @return Response
194 | */
195 | public function put($url = null, $data = null)
196 | {
197 | return $this->send($url, $data, 'PUT');
198 | }
199 |
200 | /**
201 | * Delete request
202 | *
203 | * @param string|array $url
204 | * @param null $data
205 | * @return Response
206 | */
207 | public function delete($url = null, $data = null)
208 | {
209 | return $this->send($url, $data, 'DELETE');
210 | }
211 |
212 | /**
213 | * Send request
214 | *
215 | * @param string|array $url
216 | * @param array $data
217 | * @param string $method
218 | * @return Response|bool
219 | */
220 | public function send($url = null, $data = null, $method = null)
221 | {
222 | $options = array();
223 | if ($url) $options = is_array($url) ? $url : array('url' => $url);
224 | if ($data && is_array($data)) $options['form'] = $data;
225 | if ($method) $options['method'] = $method;
226 |
227 | $this->options = $options + $this->options;
228 |
229 | if ($this->parseOptions() === false) {
230 | return false;
231 | }
232 |
233 | $options = $this->options;
234 | $url = parse_url($options['url']);
235 |
236 | $ch = curl_init();
237 |
238 | curl_setopt($ch, CURLOPT_URL, $options['url']);
239 | curl_setopt($ch, CURLOPT_HEADER, true);
240 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
241 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $options['method']);
242 | if ($url['scheme'] == 'https') {
243 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
244 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);
245 | }
246 | if ($options['timeout'] > 0) curl_setopt($ch, CURLOPT_TIMEOUT, $options['timeout']);
247 | if ($options['body']) {
248 | curl_setopt($ch, CURLOPT_POSTFIELDS, $options['body']);
249 | }
250 | if ($options['headers']) {
251 | $headers = array();
252 | foreach ($options['headers'] as $key => $value) {
253 | $headers[] = "$key: $value";
254 | }
255 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
256 | }
257 |
258 | $error = false;
259 | if (($response = curl_exec($ch)) === false) {
260 | $error = curl_error($ch);
261 | }
262 | return $this->response = new Response($response, $error);
263 | }
264 |
265 | /**
266 | * Parse the options and data
267 | */
268 | protected function parseOptions()
269 | {
270 | $options = & $this->options;
271 | if (!$options['url'] || !$options['method']) {
272 | $this->error = "Options url, method is required";
273 | return false;
274 | }
275 |
276 | if ($options['method'] !== 'GET') {
277 | if ($options['files']
278 | || isset($options['headers']['content-type'])
279 | && strpos($options['headers']['content-type'], 'multipart/form-data') === 0
280 | ) {
281 | list($boundary, $body) = self::buildMultiForm($options['form'], $options['files']);
282 | $options['headers']['content-type'] = 'multipart/form-data; boundary=' . $boundary;
283 | $options['body'] = $body;
284 | } elseif ($options['form']) {
285 | $options['headers']['content-type'] = 'application/x-www-form-urlencoded';
286 | $options['body'] = http_build_query($options['form']);
287 | }
288 | } else if ($options['form']) {
289 | $options['url'] = $options['url'] . (strpos($options['url'], '?') ? '&' : '?') . http_build_query($options['form']);
290 | }
291 |
292 | if (!empty($options['headers']['content-type']) && $options['encoding']) {
293 | $options['headers']['content-type'] .= '; charset=' . $options['encoding'];
294 | }
295 | return true;
296 | }
297 |
298 | /**
299 | * Parse multi form
300 | *
301 | * @param array $form
302 | * @param $files
303 | * @throws \InvalidArgumentException
304 | * @return array
305 | */
306 | protected static function buildMultiForm($form, $files)
307 | {
308 | $data = array();
309 | $boundary = md5(uniqid());
310 |
311 | foreach ($form as $name => $val) {
312 | $data[] = '--' . $boundary;
313 | $data[] = "Content-Disposition: form-data; name=\"$name\"";
314 | $data[] = '';
315 | $data[] = $val;
316 | }
317 |
318 | foreach ($files as $name => $file) {
319 | $data[] = '--' . $boundary;
320 | $filename = str_replace(array("\\", "\""), array("\\\\", "\\\""), $file[0]);
321 | $data[] = "Content-Disposition: form-data; name=\"$name\"; filename=\"$filename\"";
322 | $data[] = 'Content-Type: application/octet-stream';
323 | $data[] = '';
324 | $data[] = $file[1];
325 | }
326 |
327 | $data[] = '--' . $boundary . '--';
328 | $data[] = '';
329 |
330 | $body = implode("\r\n", $data);
331 | return array($boundary, $body);
332 | }
333 | }
--------------------------------------------------------------------------------
/lib/Qiniu/Response.php:
--------------------------------------------------------------------------------
1 | parse($response))) {
22 | foreach ($parsed as $key => $value) {
23 | $this->{$key} = $value;
24 | }
25 | }
26 | $this->error = $error;
27 | }
28 |
29 | /**
30 | * Parse response
31 | *
32 | * @param $response
33 | * @return array
34 | */
35 | public static function parse($response)
36 | {
37 | $body_pos = strpos($response, "\r\n\r\n");
38 | $header_string = substr($response, 0, $body_pos);
39 | if (strpos($header_string, 'HTTP/1.1 100 Continue') !== false) {
40 | $head_pos = $body_pos + 4;
41 | $body_pos = strpos($response, "\r\n\r\n", $head_pos);
42 | $header_string = substr($response, $head_pos, $body_pos - $head_pos);
43 | }
44 | $header_lines = explode("\r\n", $header_string);
45 |
46 | $headers = array();
47 | $code = false;
48 | $body = false;
49 | $protocol = null;
50 | $message = null;
51 | $data = array();
52 |
53 | foreach ($header_lines as $index => $line) {
54 | if ($index === 0) {
55 | preg_match("/^(HTTP\/\d\.\d) (\d{3}) (.*?)$/", $line, $match);
56 | list(, $protocol, $code, $message) = $match;
57 | $code = (int)$code;
58 | continue;
59 | }
60 | list($key, $value) = explode(":", $line);
61 | $headers[strtolower(trim($key))] = trim($value);
62 | }
63 |
64 | if (is_numeric($code)) {
65 | $body_string = substr($response, $body_pos + 4);
66 | if (!empty($headers['transfer-encoding']) && $headers['transfer-encoding'] == 'chunked') {
67 | $body = self::decodeChunk($body_string);
68 | } else {
69 | $body = (string)$body_string;
70 | }
71 | $result['header'] = $headers;
72 | }
73 |
74 | // 自动解析数据
75 | if (strpos($headers['content-type'], 'json')) {
76 | $data = json_decode($body, true);
77 | }
78 |
79 | return $code ? array(
80 | 'code' => $code,
81 | 'body' => $body,
82 | 'headers' => $headers,
83 | 'message' => $message,
84 | 'protocol' => $protocol,
85 | 'data' => $data
86 | ) : false;
87 | }
88 |
89 | /**
90 | * Get header
91 | *
92 | * @param string $key
93 | * @param string $default
94 | * @return string
95 | */
96 | public function header($key, $default = null)
97 | {
98 | $key = strtolower($key);
99 | return !isset($this->headers[$key]) ? $this->headers[$key] : $default;
100 | }
101 |
102 | /**
103 | * Is error?
104 | *
105 | * @return bool
106 | */
107 | public function error()
108 | {
109 | return !!$this->error;
110 | }
111 |
112 |
113 | /**
114 | * Is response cachable?
115 | *
116 | * @return bool
117 | */
118 | public function cachable()
119 | {
120 | return $this->code >= 200 && $this->code < 300 || $this->code == 304;
121 | }
122 |
123 | /**
124 | * Is empty?
125 | *
126 | * @return bool
127 | */
128 | public function bare()
129 | {
130 | return in_array($this->code, array(201, 204, 304));
131 | }
132 |
133 | /**
134 | * Is 200 ok?
135 | *
136 | * @return bool
137 | */
138 | public function ok()
139 | {
140 | return $this->code === 200;
141 | }
142 |
143 | /**
144 | * Is successful?
145 | *
146 | * @return bool
147 | */
148 | public function success()
149 | {
150 | return $this->code >= 200 && $this->code < 300;
151 | }
152 |
153 | /**
154 | * Is redirect?
155 | *
156 | * @return bool
157 | */
158 | public function redirect()
159 | {
160 | return in_array($this->code, array(301, 302, 303, 307));
161 | }
162 |
163 | /**
164 | * Is forbidden?
165 | *
166 | * @return bool
167 | */
168 | public function forbidden()
169 | {
170 | return $this->code === 403;
171 | }
172 |
173 | /**
174 | * Is found?
175 | *
176 | * @return bool
177 | */
178 | public function notFound()
179 | {
180 | return $this->code === 404;
181 | }
182 |
183 | /**
184 | * Is client error?
185 | *
186 | * @return bool
187 | */
188 | public function clientError()
189 | {
190 | return $this->code >= 400 && $this->code < 500;
191 | }
192 |
193 | /**
194 | * Is server error?
195 | *
196 | * @return bool
197 | */
198 | public function serverError()
199 | {
200 | return $this->code >= 500 && $this->code < 600;
201 | }
202 |
203 | /**
204 | * Decode chunk
205 | *
206 | * @param $str
207 | * @return string
208 | */
209 | protected static function decodeChunk($str)
210 | {
211 | $body = '';
212 | while ($str) {
213 | $chunk_pos = strpos($str, "\r\n") + 2;
214 | $chunk_size = hexdec(substr($str, 0, $chunk_pos));
215 | $str = substr($str, $chunk_pos);
216 | $body .= substr($str, 0, $chunk_size);
217 | }
218 | return $body;
219 | }
220 | }
--------------------------------------------------------------------------------
/lib/Qiniu/Result.php:
--------------------------------------------------------------------------------
1 | data = $response->data;
20 | if ($response->error) {
21 | $this->error = $response->error;
22 | } else if (!empty($this->data['error'])) {
23 | $this->error = $this->data['error'];
24 | }
25 | $this->response = $response;
26 | $this->debug = array(
27 | 'log' => $response->headers['x-log'],
28 | 'id' => isset($response->headers['x-reqid']) ? $response->headers['x-reqid'] : null
29 | );
30 | } else if ($request instanceof Request) {
31 | $this->data = false;
32 | $this->error = $request->error;
33 | }
34 | }
35 |
36 | /**
37 | * Is ok?
38 | *
39 | * @return bool
40 | */
41 | public function ok()
42 | {
43 | return !$this->error;
44 | }
45 |
46 | /**
47 | * Alias data
48 | *
49 | * @return bool
50 | */
51 | public function toArray()
52 | {
53 | return $this->data;
54 | }
55 |
56 | /**
57 | * (PHP 5 >= 5.0.0)
58 | * Whether a offset exists
59 | *
60 | * @link http://php.net/manual/en/arrayaccess.offsetexists.php
61 | * @param mixed $offset
62 | * An offset to check for. 63 | *
64 | * @return boolean true on success or false on failure. 65 | * 66 | *
67 | * The return value will be casted to boolean if non-boolean was returned.
68 | */
69 | public function offsetExists($offset)
70 | {
71 | return isset($this->data[$offset]);
72 | }
73 |
74 | /**
75 | * (PHP 5 >= 5.0.0)
76 | * Offset to retrieve
77 | *
78 | * @link http://php.net/manual/en/arrayaccess.offsetget.php
79 | * @param mixed $offset
80 | * The offset to retrieve. 81 | *
82 | * @return mixed Can return all value types. 83 | */ 84 | public function offsetGet($offset) 85 | { 86 | isset($this->data[$offset]) ? $this->data[$offset] : null; 87 | } 88 | 89 | /** 90 | * (PHP 5 >= 5.0.0)95 | * The offset to assign the value to. 96 | *
97 | * @param mixed $value98 | * The value to set. 99 | *
100 | * @return void 101 | */ 102 | public function offsetSet($offset, $value) 103 | { 104 | $this->data[$offset] = $value; 105 | } 106 | 107 | /** 108 | * (PHP 5 >= 5.0.0)113 | * The offset to unset. 114 | *
115 | * @return void 116 | */ 117 | public function offsetUnset($offset) 118 | { 119 | unset($this->data[$offset]); 120 | } 121 | } -------------------------------------------------------------------------------- /lib/Qiniu/Util.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |