Test Failed
Push — master ( fe1dc2...731208 )
by sabaku
09:50 queued 05:37
created

Upyun::getMimetype()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
ccs 0
cts 0
cp 0
crap 6
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * 又拍云 PHP-SDK
4
 */
5
namespace Upyun;
6
7
use Upyun\Api\Rest;
8
9
use GuzzleHttp\Client;
10
use GuzzleHttp\Psr7;
11
use GuzzleHttp;
12
13
/**
14
 * Class Upyun
15
 *
16
 * 又拍云云存储、云处理接口
17
 *
18
 * Upyun 类实现了又拍云云存储和云处理的所有接口,通过该类可以实现文件上传、下载;图片视频等多媒体资源云处理。
19
 * 本文档中,提到的"服务"是指又拍云文件加速回又拍云源类型的服务(即原先的存储类空间)。
20
 *
21
 * @package Upyun
22
 */
23
class Upyun
24
{
25
26
    /**
27
     * @var Config: 服务配置
28
     */
29
    protected $config;
30
31
    /**
32
     * Upyun constructor.
33
     *
34
     * @param Config $config 服务配置
35
     */
36 1
    public function __construct(Config $config)
37
    {
38 1
        $this->setConfig($config);
39 1
    }
40
41
    /**
42
     * 配置服务信息
43
     *
44
     * 当需要操作的新的服务时,使用该方法传入新的服务配置即可
45
     *
46
     * @param Config $config 服务配置
47
     *
48
     * @return $this
49
     */
50 1
    public function setConfig(Config $config)
51
    {
52 1
        $this->config = $config;
53 1
        return $this;
54
    }
55
56
    /**
57
     * 上传一个文件到又拍云存储
58
     *
59
     * 上传的文件格式支持文件流或者字符串方式上传。除简单的文件上传外,针对多媒体资源(图片、音视频),还可以设置同步/异步预处理多媒体资源,例如:图片的裁剪缩放,音视频的转码截图等等众多又拍云强大的云处理功能
60
     *
61
     * @param string $path 被上传的文件在又拍云存储服务中保存的路径
62
     * @param string|resource $content 被上传的文件内容(字符串),或者打开该文件获得的文件句柄(文件流)。当上传本地大文件时,推荐使用文件流的方式上传
63
     * @param array $params 上传文件时,附加的自定义参数。支持 Content-MD5 Content-Type Content-Secret 等,详见 [上传参数](http://docs.upyun.com/api/rest_api/#_2),例如:
64
     * - 设置文件[保护秘钥](http://docs.upyun.com/api/rest_api/#Content-Secret) `write($path, $content, array('Content-Secret' => 'my-secret'))`;
65
     * - 添加[文件元信息](http://docs.upyun.com/api/rest_api/#metadata) `write($path, $content, array('X-Upyun-Meta-Foo' =>
66
     * 'bar'))`
67
     * - [图片同步预处理](http://docs.upyun.com/cloud/image/#_5) `write($path, $content, array('x-gmkerl-thumb' => '/format/png'))`
68
     * @param bool $withAsyncProcess  默认为 `false`,当上传图片或者音视频资源时,可以设置该参数为 `true`,开启图片音视频的[异步处理功能](http://docs.upyun.com/api/form_api/#_6) ,例如:
69
     *```
70
     * // 以下参数会将新上传的图片,再异步生成另一份 png 格式的图片,原图不受影响
71
     * write($path, $content, array(
72
     *    'apps' => array(
73
     *        array(
74
     *            'name' => 'thumb',         //异步图片处理任务
75
     *            'x-gmkerl-thumb' => '/format/png', // 格式化图片为 png 格式
76
     *            'save_as': '/iamge/png/new.png',   // 处理成功后的图片保存路径
77
     *            'notify_url': 'http://your.notify.url'  // 异步任务完成后的回调地址
78
     *        )
79
     *    )
80
     * ), true);
81
     *```
82
     *
83
     *
84
     *
85
     * @return array|bool 若文件是图片则返回图片基本信息,如:`array('x-upyun-width' => 123, 'x-upyun-height' => 50, 'x-upyun-frames'
86
     * => 1, 'x-upyun-file-type' => 'JPEG')`,否则返回空数组。当使用异步预处理功能时,返回结果为布尔值,成功为 `true`。
87
     *
88
     * @throws \Exception 上传失败时,抛出异常
89
     */
90 10
    public function write($path, $content, $params = array(), $withAsyncProcess = false)
91
    {
92 10
        if (!$content) {
93
            throw new \Exception('write content can not be empty.');
94
        }
95
96 10
        $upload = new Uploader($this->config);
97 10
        $response = $upload->upload($path, $content, $params, $withAsyncProcess);
98 9
        if ($withAsyncProcess) {
99 1
            return $response;
100
        }
101 8
        return Util::getHeaderParams($response->getHeaders());
102
    }
103
104
    /**
105
     * 读取云存储文件/目录内容
106
     *
107
     * @param string $path 又拍云存储中的文件或者目录路径
108
     * @param resource $saveHandler 文件内容写入本地文件流。例如 `$saveHandler = fopen('/local/file', 'w')
109
     * `。当设置该参数时,将以文件流的方式,直接将又拍云中的文件写入本地的文件流,或其他可以写入的流
110
     * @param array $params  可选参数,读取目录内容时,需要设置三个参数: `X-List-Iter` 分页开始位置(第一页不需要设置),`X-List-Limit` 获取的文件数量(默认 100,最大
111
     * 10000),`X-List-Order` 结果以时间正序或者倒序
112
     *
113
     * @return mixed $return 当读取文件且没有设置 `$saveHandler` 参数时,返回一个字符串类型,表示文件内容;设置了 `$saveHandler` 参数时,返回布尔值
114
     * `true`。当读取目录时,返回一个数组,表示目录下的文件列表。目录下文件内容过多时,需要通过判断返回数组中的 `is_end` 属性,进行分页读取内容
115
     *
116
     * @throws \Exception
117
     */
118 3
    public function read($path, $saveHandler = null, $params = array())
119
    {
120 3
        $req = new Rest($this->config);
121 3
        $response = $req->request('GET', $path)
122 3
            ->withHeaders($params)
123 3
            ->send();
124
125
126 2
        $params = Util::getHeaderParams($response->getHeaders());
127
128
129 2
        if (! isset($params['x-upyun-list-iter'])) {
130 1
            if (is_resource($saveHandler)) {
131 1
                Psr7\copy_to_stream($response->getBody(), Psr7\stream_for($saveHandler));
132 1
                return true;
133
            } else {
134 1
                return $response->getBody()->getContents();
135
            }
136
        } else {
137 1
            $files = Util::parseDir($response->getBody()->getContents());
138 1
            return array('files' => $files, 'is_end' => $params['x-upyun-list-iter'] === 'g2gCZAAEbmV4dGQAA2VvZg', 'iter' => $params['x-upyun-list-iter']);
139
        }
140
    }
141
142
    /**
143
     * 判断文件是否存在于又拍云存储
144
     *
145
     * 注意: 对刚删除的文件, 立即调用该方法可能会返回 true, 因为服务端执行删除操作后可能会有很短暂的延迟.
146
     *
147
     * @param string $path 云存储的文件路径
148
     *
149
     * @return bool 存在时返回 `true`,否则返回 `false`
150
     * @throws \Exception
151
     */
152 2
    public function has($path)
153
    {
154 2
        $req = new Rest($this->config);
155
        try {
156 2
            $req->request('HEAD', $path)
157 2
                            ->send();
158 2
        } catch (GuzzleHttp\Exception\BadResponseException $e) {
159 1
            $statusCode = $e->getResponse()->getStatusCode();
160 1
            if ($statusCode === 404) {
161 1
                return false;
162
            } else {
163
                throw $e;
164
            }
165
        }
166
167 2
        return true;
168
    }
169
170
    /**
171
     * 获取云存储文件/目录的基本信息
172
     *
173
     * @param string $path 云存储的文件路径
174
     * @param array $otherHeaders 设置了后,方法将返回其他 http header 中的信息,默认为空
175
     *
176
     * @return array 返回一个数组,包含以下 key
177
     * - `x-upyun-file-type` 当 $path 是目录时,值为 *folder*,当 $path 是文件时,值为 *file*,
178
     * - `x-upyun-file-size` 文件大小
179
     * - `x-upyun-file-date` 文件的创建时间
180 1
     */
181
    public function info($path, $otherHeaders = array())
182 1
    {
183 1
        $req = new Rest($this->config);
184 1
        $response = $req->request('HEAD', $path)
185 1
                        ->send();
186
        return Util::getHeaderParams($response->getHeaders(), $otherHeaders);
187
    }
188
189
    public function getMimetype($path)
190
    {
191
        $params = $this->info($path, array('content-type'));
192
        if (isset($params['content-type'])) {
193
            return explode(';', $params['content-type'])[0];
194
        }
195
        return '';
196
    }
197 4
198
    /**
199 4
     * 删除文件或者目录
200 4
     *
201 4
     * @param string $path 文件或目录在又拍云存储的路径
202
     * @param bool $async 是否异步删除,默认为 false,表示同步删除。当需要批量删除大量文件时,必须选择异步删除
203
     *
204 4
     * @return bool 删除成功返回 true,否则 false
205 3
     * @throws \Exception 删除不存在的文件将会抛出异常
206
     */
207
    public function delete($path, $async = false)
208
    {
209
        $req = new Rest($this->config);
210
        $req->request('DELETE', $path);
211
        if ($async) {
212
            $req->withHeader('x-upyun-async', 'true');
213
        }
214
        $res = $req->send();
215
        return $res->getStatusCode() === 200;
216 2
    }
217
218 2
    /**
219 2
     * 创建目录
220 2
     *
221 2
     * @param string $path 需要在又拍云存储创建的目录路径
222 2
     *
223 2
     * @return bool 创建成功返回 true,否则返回 false
224
     * @throws \Exception
225
     */
226 View Code Duplication
    public function createDir($path)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
227
    {
228
        $path = rtrim($path, '/') . '/';
229
        $req = new Rest($this->config);
230
        $res = $req->request('POST', $path)
231
            ->withHeader('folder', 'true')
232
            ->send();
233
        return $res->getStatusCode() === 200;
234 1
    }
235
236 1
    /**
237
     * 删除文件或者目录
238
     *
239
     * @param string $path 需要被删除的云存储文件或目录路径
240
     *
241
     * @return bool 成功返回 true,否则 false
242
     * @throws \Exception
243
     */
244
    public function deleteDir($path)
245
    {
246 1
        return $this->delete($path);
247
    }
248 1
249 1
    /**
250 1
     * 获取目录下存储使用量
251 1
     *
252
     * @param string $path 云存储目录路径,默认为根目录,表示整个云存储服务使用的空间大小
253 1
     * @return string 存储使用量,单位字节
254
     * @throws \Exception
255
     */
256 View Code Duplication
    public function usage($path = '/')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258
        $path = rtrim($path, '/') . '/';
259
        $req = new Rest($this->config);
260
        $response = $req->request('GET', $path . '?usage')
261
            ->send();
262
263 1
        return $response->getBody()->getContents();
264
    }
265 1
266 1
    /**
267
     * 刷新缓存
268
     *
269
     * @param array|string $urls 需要刷新的文件 url 列表
270 1
     *
271 1
     * @return array 刷新失败的 url 列表,若全部刷新成功则为空数组
272 1
     */
273 1
    public function purge($urls)
274 1
    {
275 1
        $urlString = $urls;
276 1
        if (is_array($urls)) {
277 1
            $urlString = implode("\n", $urls);
278 1
        }
279
280
        $client = new Client([
281
            'timeout' => $this->config->timeout
282
        ]);
283
        $response = $client->request('POST', Config::ED_PURGE, [
284
            'headers' =>  Signature::getPurgeSignHeader($this->config, $urlString),
285
            'form_params' => ['purge' => $urlString]
286
        ]);
287
        $result = json_decode($response->getBody()->getContents(), true);
288
        return $result['invalid_domain_of_url'];
289
    }
290
291
    /**
292
     * 异步云处理
293
     *
294
     * 该方法是基于[又拍云云处理](http://docs.upyun.com/cloud/) 服务实现,可以实现音视频的转码、切片、剪辑;文件的压缩解压缩;文件拉取功能
295
     * 所有需要调用该方法处理的资源,必须已经上传到云存储服务,未上传到云存储的文件,同时需要云处理功能,请使用 `write` 方法。
296
     * 例如视频转码:
297
     * ```
298
     *  process($source, array(
299
     *    array(
300
     *        'type' => 'video',  // video 表示视频任务, audio 表示音频任务
301
     *        'avopts' => '/s/240p(4:3)/as/1/r/30', // 处理参数,`s` 表示输出的分辨率,`r` 表示视频帧率,`as` 表示是否自动调整分辨率
302
     *        'save_as' => '/video/240/new.mp4', // 新视频在又拍云存储的保存路径
303
     *    ),
304
     *    ... // 同时还可以添加其他任务
305
     * ))
306
     * ```
307
     * 注意,被处理的资源需要已经上传到又拍云云存储
308
     *
309
     * @param string $source 需要预处理的图片、音视频资源在又拍云存储的路径
310 1
     * @param array $tasks 需要处理的任务
311
     *
312 1
     * @return array 任务 ID,提交了多少任务,便会返回多少任务 ID,与提交任务的顺序保持一致。可以通过任务 ID 查询处理进度。格式如下:
313 1
     * ```
314
     * array(
315
     *     '35f0148d414a688a275bf915ba7cebb2',
316
     *     '98adbaa52b2f63d6d7f327a0ff223348',
317
     * )
318
     * ```
319
     */
320
    public function process($source, $tasks)
321
    {
322
        $video = new Api\Pretreat($this->config);
323
        return $video->process($source, $tasks);
324
    }
325
326
    /**
327
     * 查询异步云处理任务进度
328
     *
329
     * 根据 `process` 方法返回的任务 ID,通过该访问查询处理进度
330
     *
331
     * @param array $taskIds 任务 ID
332 1
     *
333
     * @return bool|array 查询失败返回布尔值 `false`,否则返回每个任务的百分比进度信息,格式如下:
334 1
     * ```
335 1
     * array(
336
     *     '35f0148d414a688a275bf915ba7cebb2' => 100,  // 100 表示任务完成
337
     *     'c3103189fa906a5354d29bd807e8dc51' => 35,
338
     *     '98adbaa52b2f63d6d7f327a0ff223348' => null, // null 表示任务未开始,或异常
339
     * )
340
     * ```
341
     */
342
    public function queryProcessStatus($taskIds)
343
    {
344
        $video = new Api\Pretreat($this->config);
345
        return $video->query($taskIds, '/status/');
346
    }
347
348
    /**
349
     *  查询异步云处理任务结果
350
     *
351
     * 根据 `process` 方法返回的任务 ID,通过该访问查询处理结果,会包含每个任务详细信息
352
     * @param array $taskIds 任务 ID
353
     *
354
     * @return bool|mixed 查询失败返回 `false`,否则返回每个任务的处理结果,格式如下:
355
     * ```
356
     * array(
357
     *    '9d9c32b63a1034834e77672c6f51f661' => array(
358
     *         'path' => array('/v2.mp4'),
359 1
     *         'signature' => '4042c1f07f546d28',
360
     *         'status_code' => 200,
361 1
     *         'bucket_name' => 'your_storage_bucket',
362 1
     *         'description' => 'OK',
363
     *         'task_id' => '9d9c32b63a1034834e77672c6f51f661',
364
     *         'timestamp' => 1472010684
365
     *    )
366
     * )
367
     * ```
368
     */
369
    public function queryProcessResult($taskIds)
370
    {
371
        $video = new Api\Pretreat($this->config);
372
        return $video->query($taskIds, '/result/');
373
    }
374
}
375