├── .editorconfig
├── .gitattributes
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── RoboFile.php
├── composer.json
└── src
└── Upyun
├── Api
├── Form.php
├── Pretreat.php
├── Rest.php
└── SyncVideo.php
├── Config.php
├── Signature.php
├── Uploader.php
├── Upyun.php
└── Util.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*.{php,html}]
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 | /examples export-ignore
4 | /tests export-ignore
5 | /doc.md export-ignore
6 | /phpunit.xml export-ignore
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | vendor
4 | composer.lock
5 | release*
6 | .php_cs.cache
7 | coverage.clover
8 |
--------------------------------------------------------------------------------
/.scrutinizer.yml:
--------------------------------------------------------------------------------
1 | build:
2 | environment:
3 | php:
4 | version: 5.5
5 | dependencies:
6 | before:
7 | - composer install
8 | tests:
9 | override:
10 | -
11 | command: phpunit
12 | coverage:
13 | file: coverage.clover
14 | format: clover
15 | filter:
16 | paths:
17 | - 'src/*'
18 | - 'tests/*'
19 |
20 | tools:
21 | php_code_sniffer:
22 | config:
23 | standard: PSR2
24 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 3.3.0
2 |
3 | - 增加同步视频处理功能
4 |
5 | ## 3.0.0
6 |
7 | - 重写 API 接口,不兼容 2.x 版本
8 | - 集合分块、刷新、视频预处理功能
9 |
10 | ## 2.2.0
11 |
12 | - 增加 composer 支持,特别感谢 [@totoleo](https://github.com/totoleo) 将 `upyun/sdk` 仓库源修改为 UPYUN 官方项目地址
13 | - 移除不再推荐使用的 API:`rmDir deleteFile readDir getWritedFileInfo`),建议使用推荐方法替代
14 | - note: `2.1.0` 版本之前已经被 [@totoleo](https://github.com/totoleo) 使用
15 |
16 | ## 2.0.0
17 |
18 | - 使用1.0.x系列版本SDK的用户,注意原有部分方法已经不再推荐使用(`@deprecated`标注),但是出于兼容考虑目前任然保留,建议更新升级程序使用新版SDK提供的方法。
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 UPYUN
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 又拍云 SDK for PHPer
2 | [](https://scrutinizer-ci.com/g/upyun/php-sdk/build-status/master) [](https://scrutinizer-ci.com/g/upyun/php-sdk/?branch=master) [](https://scrutinizer-ci.com/g/upyun/php-sdk/?branch=master)
3 |
4 | 又拍云 PHP SDK,封装了[又拍云功能丰富的开放 API](http://docs.upyun.com/api/) ,帮助开发者快速对接文件云端存储、图片音视频云处理、智能鉴黄等功能
5 |
6 | - [功能列表](#list)
7 | - [使用说明](#use-instructions)
8 | - [安装](#install)
9 | - [文档](#doc)
10 | - [示例](#usage)
11 | - [贡献代码](#contribute)
12 | - [社区](#community)
13 | - [许可证](#license)
14 |
15 |
16 |
17 | ## 功能列表
18 |
19 | SDK 包含如下功能
20 |
21 | - 基于 [rest api](http://docs.upyun.com/api/rest_api/)
22 | - 文件上传下载、目录创建删除等云存储基本操作
23 | - [断点续传](http://docs.upyun.com/api/rest_api/#_3)
24 | - 基于 [form api](http://docs.upyun.com/api/form_api/)
25 | - 文件客户端上传 见`examples/client-upload`
26 | - 上传预处理操作
27 | - [同步音频处理](http://docs.upyun.com/cloud/sync_audio/)
28 | - [文档转换](http://docs.upyun.com/cloud/uconvert/)
29 | - [异步图片音视频处理](http://docs.upyun.com/api/form_api/#_7)
30 | - [异步图片智能鉴黄](http://docs.upyun.com/ai/audit/)
31 | - [异步云处理](http://docs.upyun.com/cloud/)
32 | - [视频音频](http://docs.upyun.com/cloud/av/)异步转码、切片、截图、水印、剪辑、拼接等功能
33 | - [文件异步解压缩](http://docs.upyun.com/cloud/unzip/)
34 | - [文件异步拉取](http://docs.upyun.com/cloud/spider/)
35 | - [异步图片拼接](http://docs.upyun.com/cloud/async_image/)
36 | - [同步视频处理](http://docs.upyun.com/cloud/sync_video/):m3u8 文件同步拼接剪辑、其他视频文件同步截图功能
37 | - [缓存刷新](http://docs.upyun.com/api/purge/)
38 |
39 | *功能列表中的异步操作,均可以设置异步回调通知地址,接收异步处理结果*
40 | *如果需要测试回调功能,可以通过[又拍云回调服务](https://hooks.upyun.com/)创建一个临时回调地址*
41 |
42 |
43 | ## 使用说明
44 |
45 |
46 | ### 安装
47 |
48 | #### PHP >= 5.5
49 |
50 | 1.使用 `composer` 安装
51 |
52 | 推荐使用该方法安装,成为优雅的 PHPer :fire:
53 |
54 | 建议使用速度很快的国内[全量镜像](https://pkg.phpcomposer.com/#how-to-use-packagist-mirror)([又拍云赞助](https://pkg.phpcomposer.com/#donation))
55 |
56 | ```
57 | composer require upyun/sdk
58 | ```
59 |
60 | 2.如果不适应 `composer` 管理,可以直接下载[压缩包](https://github.com/upyun/php-sdk/releases)(注意需要下载 `php-sdk-版本号.zip` 格式的 zip 压缩包,不是 Source code 源码压缩包),解压后,项目中添加如下代码:
61 |
62 | ```
63 | require_once '/path/to/php-sdk/vendor/autoload.php';
64 | ```
65 |
66 | ### 文档
67 |
68 | 详细文档见 [doc.md](doc.md)
69 |
70 |
71 | ### 示例
72 |
73 | 先初始化又拍云服务配置:
74 |
75 | ```php
76 | require_once('vendor/autoload.php'); // 只针对使用 composer 安装
77 | // require_once '/path/to/php-sdk/vendor/autoload.php'; // 针对压缩包安装
78 |
79 | use Upyun\Upyun;
80 | use Upyun\Config;
81 | $serviceConfig = new Config('yourServiceName', 'yourOperatorName', 'yourOperatorPwd');
82 | $client = new Upyun($serviceConfig);
83 | ```
84 |
85 | #### 字符串写入又拍云服务器
86 |
87 | ```
88 | $client->write('/save/path', 'file content');
89 | ```
90 |
91 | #### 文件流写入又拍云服务器
92 |
93 | ```
94 | $file = fopen('/local/path/file', 'r');
95 | $client->write('/save/path', $file);
96 | ```
97 |
98 | #### 使用并行式断点续传上传文件
99 |
100 | ```
101 | $serviceConfig->setUploadType('BLOCK_PARALLEL');
102 | $client = new Upyun($serviceConfig);
103 | $file = fopen('/local/path/file', 'r');
104 | $client->write('/save/path', $file);
105 | ```
106 |
107 | #### 上传图片并转换格式为 `png`,详见[上传作图](http://docs.upyun.com/cloud/image/#_2)
108 |
109 | ```
110 | $file = fopen('/local/path/image.jpg', 'r');
111 | $client->write('/save/image.png', $file, array('x-gmkerl-thumb' => '/format/png'));
112 | ```
113 |
114 | #### 下载文件并保存到本地
115 |
116 | ```
117 | $saveLocal = fopen('/local/path/image.jpg', 'w');
118 | // 第二个参数不传时,read 方法将直接返回文件内容
119 | $client->read('/remote/server/image.png', $saveLocal);
120 | ```
121 |
122 |
123 | ## 贡献代码
124 | 1. Fork
125 | 2. 为新特性创建一个新的分支
126 | 3. 发送一个 pull request 到 master 分支
127 |
128 |
129 | ## 社区
130 |
131 | - [问答社区](http://segmentfault.com/upyun)
132 | - [微博](http://weibo.com/upaiyun)
133 |
134 |
135 | ## 许可证
136 |
137 | UPYUN PHP-SDK 基于 MIT 开源协议
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/RoboFile.php:
--------------------------------------------------------------------------------
1 | collectionBuilder();
20 | $workingPath = __DIR__ . DIRECTORY_SEPARATOR . $collection->workDir("release");
21 | $collection->taskExec("composer create-project {$package} {$name} {$version}")
22 | ->dir($workingPath)
23 | ->arg('--prefer-dist')
24 | ->arg('--no-dev')
25 | ->arg('-vvv')
26 | ->taskExec('composer dump-autoload --optimize')
27 | ->dir($workingPath . DIRECTORY_SEPARATOR . $name)
28 | ->arg('-vvv');
29 | $collection->run();
30 |
31 | $zipFile = "release/{$name}-{$version}.zip";
32 | $this->_remove($zipFile);
33 | $this->taskPack($zipFile)
34 | ->addDir("php-sdk", __DIR__ . "/release/php-sdk")
35 | ->run();
36 | $this->_deleteDir("release/$name");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "upyun/sdk",
3 | "description": "UPYUN sdk for php",
4 | "keywords": ["UPYUN", "sdk"],
5 | "type": "library",
6 | "minimum-stability": "stable",
7 | "homepage": "https://github.com/upyun/php-sdk/",
8 | "license": "MIT",
9 | "require": {
10 | "php": ">=5.5.0",
11 | "ext-curl": "*",
12 | "guzzlehttp/guzzle": "~6.0"
13 | },
14 | "require-dev": {
15 | "phpunit/phpunit": "~4.0",
16 | "phpdocumentor/phpdocumentor": "^2.9",
17 | "consolidation/robo": "^1.0"
18 | },
19 | "autoload": {
20 | "psr-4": { "Upyun\\": "src/Upyun/" }
21 | },
22 | "autoload-dev": {
23 | "psr-4": { "Upyun\\Tests\\": "tests/" }
24 | },
25 | "authors": [
26 | {
27 | "name": "lfeng",
28 | "email": "bonevv@gmail.com"
29 | },
30 | {
31 | "name": "lvtongda",
32 | "email": "riyao.lyu@gmail.com"
33 | },
34 | {
35 | "name": "totoleo",
36 | "email": "totoleo@163.com"
37 | },
38 | {
39 | "name": "sabakugaara",
40 | "email": "senellise@gmail.com"
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/src/Upyun/Api/Form.php:
--------------------------------------------------------------------------------
1 | config->serviceName;
15 | if (!isset($params['expiration'])) {
16 | $params['expiration'] = time() + 30 * 60 * 60; // 30 分钟
17 | }
18 |
19 | $policy = Util::base64Json($params);
20 | $method = 'POST';
21 | $signature = Signature::getBodySignature($this->config, $method, '/' . $params['service'], null, $policy);
22 | $client = new Client([
23 | 'timeout' => $this->config->timeout,
24 | ]);
25 |
26 | $response = $client->request($method, $this->endpoint, array(
27 | 'multipart' => array(
28 | array(
29 | 'name' => 'policy',
30 | 'contents' => $policy,
31 | ),
32 | array(
33 | 'name' => 'authorization',
34 | 'contents' => $signature,
35 | ),
36 | array(
37 | 'name' => 'file',
38 | 'contents' => $stream,
39 | )
40 | )
41 | ));
42 | return $response->getStatusCode() === 200;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Upyun/Api/Pretreat.php:
--------------------------------------------------------------------------------
1 | processNotifyUrl) {
20 | throw new \Exception("should config prosessNotifyUrl first.");
21 | }
22 | $this->config = $config;
23 | }
24 |
25 | public function process($tasks, $optionalParams = array())
26 | {
27 | $encodedTasks = Util::base64Json($tasks);
28 |
29 | $client = new Client([
30 | 'timeout' => $this->config->timeout,
31 | ]);
32 |
33 | $params = array(
34 | 'service' => $this->config->serviceName,
35 | 'notify_url' => $this->config->processNotifyUrl,
36 | 'tasks' => $encodedTasks,
37 | );
38 |
39 | $params = array_merge($params, $optionalParams);
40 |
41 | $path = '/pretreatment/';
42 | $method = 'POST';
43 | $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
44 |
45 | $url = $this->config->getPretreatEndPoint() . $path;
46 | $response = $client->request($method, $url, [
47 | 'headers' => $signedHeaders,
48 | 'form_params' => $params
49 | ]);
50 |
51 | $body = $response->getBody()->getContents();
52 | return json_decode($body, true);
53 | }
54 |
55 |
56 | public function query($taskIds, $path)
57 | {
58 | $client = new Client([
59 | 'timeout' => $this->config->timeout,
60 | ]);
61 |
62 | $params = array(
63 | 'service' => $this->config->serviceName,
64 | 'task_ids' => implode(',', $taskIds)
65 | );
66 | $path = $path . '?' . http_build_query($params);
67 |
68 | $method = 'GET';
69 | $url = $this->config->getPretreatEndPoint() . $path;
70 | $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
71 | $response = $client->request($method, $url, [
72 | 'headers' => $signedHeaders
73 | ]);
74 |
75 | if ($response->getStatusCode() === 200) {
76 | $body = $response->getBody()->getContents();
77 | $result = json_decode($body, true);
78 | if (is_array($result)) {
79 | return $result['tasks'];
80 | }
81 | }
82 | return false;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Upyun/Api/Rest.php:
--------------------------------------------------------------------------------
1 | config = $config;
32 | $this->endpoint = $config->getProtocol() . Config::$restApiEndPoint . '/' . $config->serviceName;
33 | }
34 |
35 | public function request($method, $storagePath)
36 | {
37 | $this->method = strtoupper($method);
38 | $this->storagePath = '/' . ltrim($storagePath, '/');
39 | return $this;
40 | }
41 |
42 |
43 | /**
44 | * @param string|resource $file
45 | *
46 | * @return $this
47 | */
48 | public function withFile($file)
49 | {
50 | $stream = Psr7\stream_for($file);
51 | $this->file = $stream;
52 |
53 | return $this;
54 | }
55 |
56 | /**
57 | * @return mixed|\Psr\Http\Message\ResponseInterface
58 | */
59 | public function send()
60 | {
61 | $client = new Client([
62 | 'timeout' => $this->config->timeout,
63 | ]);
64 |
65 | $url = $this->endpoint . $this->storagePath;
66 | $body = null;
67 | if ($this->file && $this->method === 'PUT') {
68 | $body = $this->file;
69 | }
70 |
71 | $request = new Psr7\Request(
72 | $this->method,
73 | Util::encodeURI($url),
74 | $this->headers,
75 | $body
76 | );
77 | $authHeader = Signature::getHeaderSign($this->config,
78 | $this->method,
79 | $request->getUri()->getPath()
80 | );
81 | foreach ($authHeader as $head => $value) {
82 | $request = $request->withHeader($head, $value);
83 | }
84 | $response = $client->send($request, [
85 | 'debug' => $this->config->debug
86 | ]);
87 |
88 | return $response;
89 | }
90 |
91 | public function withHeader($header, $value)
92 | {
93 | $header = strtolower(trim($header));
94 |
95 | $this->headers[$header] = $value;
96 | return $this;
97 | }
98 |
99 | public function withHeaders($headers)
100 | {
101 | if (is_array($headers)) {
102 | foreach ($headers as $header => $value) {
103 | $this->withHeader($header, $value);
104 | }
105 | }
106 | return $this;
107 | }
108 |
109 | public function toRequest()
110 | {
111 | $url = $this->endpoint . $this->storagePath;
112 | $body = null;
113 | if ($this->file && $this->method === 'PUT') {
114 | $body = $this->file;
115 | }
116 |
117 | $request = new Psr7\Request(
118 | $this->method,
119 | Util::encodeURI($url),
120 | $this->headers,
121 | $body
122 | );
123 | $authHeader = Signature::getHeaderSign($this->config,
124 | $this->method,
125 | $request->getUri()->getPath()
126 | );
127 | foreach ($authHeader as $head => $value) {
128 | $request = $request->withHeader($head, $value);
129 | }
130 | return $request;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/Upyun/Api/SyncVideo.php:
--------------------------------------------------------------------------------
1 | config = $config;
22 | }
23 |
24 | public function process($params, $path) {
25 | $client = new Client([
26 | 'timeout' => $this->config->timeout,
27 | ]);
28 |
29 | $path = '/' . $this->config->serviceName . $path;
30 | $method = 'POST';
31 | $signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
32 |
33 | $url = $this->config->getSyncVideoEndPoint() . $path;
34 | $response = $client->request($method, $url, [
35 | 'headers' => $signedHeaders,
36 | 'json' => $params
37 | ]);
38 |
39 | $body = $response->getBody()->getContents();
40 | return json_decode($body, true);
41 | }
42 | }
--------------------------------------------------------------------------------
/src/Upyun/Config.php:
--------------------------------------------------------------------------------
1 | serviceName = $serviceName;
107 | $this->bucketName = $serviceName;
108 | $this->operatorName = $operatorName;
109 | $this->setOperatorPassword($operatorPassword);
110 | $this->useSsl = false;
111 | self::$restApiEndPoint = self::ED_AUTO;
112 | }
113 |
114 | public function setOperatorPassword($operatorPassword)
115 | {
116 | $this->operatorPassword = md5($operatorPassword);
117 | }
118 |
119 | public function getFormApiKey()
120 | {
121 | if (! $this->formApiKey) {
122 | throw new \Exception('form api key is empty.');
123 | }
124 |
125 | return $this->formApiKey;
126 | }
127 |
128 | public function setFormApiKey($key)
129 | {
130 | $this->formApiKey = $key;
131 | }
132 |
133 | public function getVersion()
134 | {
135 | return $this->version;
136 | }
137 |
138 | public function getPretreatEndPoint()
139 | {
140 | return $this->getProtocol() . self::ED_VIDEO;
141 | }
142 |
143 | public function getSyncVideoEndPoint()
144 | {
145 | return $this->getProtocol() . self::ED_SYNC_VIDEO;
146 | }
147 |
148 | public function getProtocol()
149 | {
150 | return $this->useSsl ? 'https://' : 'http://';
151 | }
152 |
153 | public function setUploadType($uploadType)
154 | {
155 | $this->uploadType = $uploadType;
156 | }
157 |
158 | public function setConcurrency($concurrency)
159 | {
160 | $this->concurrency = $concurrency;
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/Upyun/Signature.php:
--------------------------------------------------------------------------------
1 | $sign,
42 | 'Date' => $gmtDate,
43 | 'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion()
44 | );
45 | return $headers;
46 | }
47 |
48 | /**
49 | * 获取请求缓存刷新接口需要的签名头
50 | *
51 | * @param Config $serviceConfig
52 | * @param $urlString
53 | *
54 | * @return array
55 | */
56 | public static function getPurgeSignHeader(Config $serviceConfig, $urlString)
57 | {
58 | $gmtDate = gmdate('D, d M Y H:i:s \G\M\T');
59 | $sign = md5("$urlString&{$serviceConfig->serviceName}&$gmtDate&{$serviceConfig->operatorPassword}");
60 | return array(
61 | 'Authorization' => "UpYun {$serviceConfig->serviceName}:{$serviceConfig->operatorName}:$sign",
62 | 'Date' => $gmtDate,
63 | 'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion() . ' (purge api)'
64 | );
65 | }
66 |
67 | /**
68 | * 获取表单 API 需要的签名,依据 body 签名规则计算
69 | * @param Config $serviceConfig
70 | * @param $method 请求方法
71 | * @param $uri 请求路径
72 | * @param $date 请求时间
73 | * @param $policy
74 | * @param $contentMd5 请求 body 的 md5
75 | *
76 | * @return array
77 | */
78 | public static function getBodySignature(Config $serviceConfig, $method, $uri, $date = null, $policy = null, $contentMd5 = null)
79 | {
80 | $data = array(
81 | $method,
82 | $uri
83 | );
84 | if ($date) {
85 | $data[] = $date;
86 | }
87 |
88 | if ($policy) {
89 | $data[] = $policy;
90 | }
91 |
92 | if ($contentMd5) {
93 | $data[] = $contentMd5;
94 | }
95 | $signature = base64_encode(hash_hmac('sha1', implode('&', $data), $serviceConfig->operatorPassword, true));
96 | return 'UPYUN ' . $serviceConfig->operatorName . ':' . $signature;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Upyun/Uploader.php:
--------------------------------------------------------------------------------
1 | config = $config;
23 | }
24 |
25 | public function upload($path, $file, $params, $withAsyncProcess)
26 | {
27 | $stream = Psr7\stream_for($file);
28 | $size = $stream->getSize();
29 | $useBlock = $this->needUseBlock($size);
30 |
31 | if ($withAsyncProcess) {
32 | $req = new Form($this->config);
33 | return $req->upload($path, $stream, $params);
34 | }
35 |
36 | if (! $useBlock) {
37 | $req = new Rest($this->config);
38 | return $req->request('PUT', $path)
39 | ->withHeaders($params)
40 | ->withFile($stream)
41 | ->send();
42 | } elseif ($this->config->uploadType === 'BLOCK_PARALLEL') {
43 | return $this->concurrentPointUpload($path, $stream, $params);
44 | } else {
45 | return $this->pointUpload($path, $stream, $params);
46 | }
47 | }
48 |
49 | /**
50 | * 串行式断点续传
51 | * @param $path
52 | * @param $stream
53 | * @param $params
54 | *
55 | * @return mixed|\Psr\Http\Message\ResponseInterface
56 | * @throws \Exception
57 | */
58 | private function pointUpload($path, $stream, $params)
59 | {
60 | $req = new Rest($this->config);
61 | $headers = array();
62 | if (is_array($params)) {
63 | foreach ($params as $key => $val) {
64 | $headers['X-Upyun-Meta-' . $key] = $val;
65 | }
66 | }
67 | $res = $req->request('PUT', $path)
68 | ->withHeaders(array_merge(array(
69 | 'X-Upyun-Multi-Stage' => 'initiate',
70 | 'X-Upyun-Multi-Type' => Psr7\mimetype_from_filename($path),
71 | 'X-Upyun-Multi-Length' => $stream->getSize(),
72 | ), $headers))
73 | ->send();
74 | if ($res->getStatusCode() !== 204) {
75 | throw new \Exception('init request failed when poinit upload!');
76 | }
77 |
78 | $init = Util::getHeaderParams($res->getHeaders());
79 | $uuid = $init['x-upyun-multi-uuid'];
80 | $blockSize = 1024 * 1024;
81 | $partId = 0;
82 | do {
83 | $fileBlock = $stream->read($blockSize);
84 | $res = $req->request('PUT', $path)
85 | ->withHeaders(array(
86 | 'X-Upyun-Multi-Stage' => 'upload',
87 | 'X-Upyun-Multi-Uuid' => $uuid,
88 | 'X-Upyun-Part-Id' => $partId
89 | ))
90 | ->withFile(Psr7\stream_for($fileBlock))
91 | ->send();
92 |
93 | if ($res->getStatusCode() !== 204) {
94 | throw new \Exception('upload request failed when poinit upload!');
95 | }
96 | $data = Util::getHeaderParams($res->getHeaders());
97 | $partId = $data['x-upyun-next-part-id'];
98 | } while ($partId != -1);
99 |
100 | $res = $req->request('PUT', $path)
101 | ->withHeaders(array(
102 | 'X-Upyun-Multi-Uuid' => $uuid,
103 | 'X-Upyun-Multi-Stage' => 'complete'
104 | ))
105 | ->send();
106 |
107 | if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
108 | throw new \Exception('end request failed when poinit upload!');
109 | }
110 | return $res;
111 | }
112 |
113 | private function needUseBlock($fileSize)
114 | {
115 | if ($this->config->uploadType === 'BLOCK' ||
116 | $this->config->uploadType === 'BLOCK_PARALLEL') {
117 | return true;
118 | } elseif ($this->config->uploadType === 'AUTO' &&
119 | $fileSize >= $this->config->sizeBoundary) {
120 | return true;
121 | } else {
122 | return false;
123 | }
124 | }
125 |
126 | /**
127 | * 并行式断点续传
128 | * @param $path
129 | * @param $stream
130 | * @param $params
131 | *
132 | * @return mixed|\Psr\Http\Message\ResponseInterface
133 | * @throws \Exception
134 | */
135 | private function concurrentPointUpload($path, $stream, $params)
136 | {
137 | $req = new Rest($this->config);
138 |
139 | $headers = array();
140 | if (is_array($params)) {
141 | foreach ($params as $key => $val) {
142 | $headers['X-Upyun-Meta-' . $key] = $val;
143 | }
144 | }
145 | $res = $req->request('PUT', $path)
146 | ->withHeaders(array_merge(array(
147 | 'X-Upyun-Multi-Disorder' => 'true',
148 | 'X-Upyun-Multi-Stage' => 'initiate',
149 | 'X-Upyun-Multi-Type' => Psr7\mimetype_from_filename($path),
150 | 'X-Upyun-Multi-Length' => $stream->getSize(),
151 | ), $headers))
152 | ->send();
153 | if ($res->getStatusCode() !== 204) {
154 | throw new \Exception('init request failed when poinit upload!');
155 | }
156 |
157 | $init = Util::getHeaderParams($res->getHeaders());
158 | $uuid = $init['x-upyun-multi-uuid'];
159 | $requests = function ($req, $path, $stream, $uuid) {
160 | $blockSize = 1024 * 1024;
161 | $total = ceil($stream->getSize() / $blockSize);
162 | for ($i = 0; $i < $total; $i++) {
163 | $fileBlock = $stream->read($blockSize);
164 | yield $req->request('PUT', $path)
165 | ->withHeaders(array(
166 | 'X-Upyun-Multi-Stage' => 'upload',
167 | 'X-Upyun-Multi-Uuid' => $uuid,
168 | 'X-Upyun-Part-Id' => $i
169 | ))
170 | ->withFile(Psr7\stream_for($fileBlock))
171 | ->toRequest();
172 | }
173 | };
174 | $client = new Client([
175 | 'timeout' => $this->config->timeout,
176 | ]);
177 | $pool = new Pool($client, $requests($req, $path, $stream, $uuid), [
178 | 'concurrency' => $this->config->concurrency,
179 | 'fulfilled' => function ($res) {
180 | if ($res->getStatusCode() !== 204) {
181 | throw new \Exception('upload request failed when poinit upload!');
182 | }
183 | },
184 | 'rejected' => function () {
185 | throw new \Exception('upload request failed when poinit upload!');
186 | },
187 | ]);
188 | $promise = $pool->promise();
189 | $promise->wait();
190 |
191 | $res = $req->request('PUT', $path)
192 | ->withHeaders(array(
193 | 'X-Upyun-Multi-Uuid' => $uuid,
194 | 'X-Upyun-Multi-Stage' => 'complete'
195 | ))
196 | ->send();
197 | if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
198 | throw new \Exception('end request failed when poinit upload!');
199 | }
200 | return $res;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/Upyun/Upyun.php:
--------------------------------------------------------------------------------
1 | setConfig($config);
65 | }
66 |
67 | /**
68 | * 配置服务信息
69 | *
70 | * 当需要操作的新的服务时,使用该方法传入新的服务配置即可
71 | *
72 | * @param Config $config 服务配置
73 | *
74 | * @return $this
75 | */
76 | public function setConfig(Config $config)
77 | {
78 | $this->config = $config;
79 | return $this;
80 | }
81 |
82 | /**
83 | * 上传一个文件到又拍云存储
84 | *
85 | * 上传的文件格式支持文件流或者字符串方式上传。除简单的文件上传外,针对多媒体资源(图片、音视频),还可以设置同步/异步预处理多媒体资源,例如:图片的裁剪缩放,音视频的转码截图等等众多又拍云强大的云处理功能
86 | *
87 | * @param string $path 被上传的文件在又拍云存储服务中保存的路径
88 | * @param string|resource $content 被上传的文件内容(字符串),或者打开该文件获得的文件句柄(文件流)。当上传本地大文件时,推荐使用文件流的方式上传
89 | * @param array $params 上传文件时,附加的自定义参数。支持 Content-MD5 Content-Type Content-Secret 等,详见 [上传参数](http://docs.upyun.com/api/rest_api/#_2),例如:
90 | * - 设置文件[保护秘钥](http://docs.upyun.com/api/rest_api/#Content-Secret) `write($path, $content, array('Content-Secret' => 'my-secret'))`;
91 | * - 添加[文件元信息](http://docs.upyun.com/api/rest_api/#metadata) `write($path, $content, array('X-Upyun-Meta-Foo' =>
92 | * 'bar'))`
93 | * - [图片同步预处理](http://docs.upyun.com/cloud/image/#_5) `write($path, $content, array('x-gmkerl-thumb' => '/format/png'))`
94 | * @param bool $withAsyncProcess 默认为 `false`,当上传图片或者音视频资源时,可以设置该参数为 `true`,开启图片音视频的[异步处理功能](http://docs.upyun.com/api/form_api/#_6) ,例如:
95 | *```
96 | * // 以下参数会将新上传的图片,再异步生成另一份 png 格式的图片,原图不受影响
97 | * write($path, $content, array(
98 | * 'apps' => array(
99 | * array(
100 | * 'name' => 'thumb', //异步图片处理任务
101 | * 'x-gmkerl-thumb' => '/format/png', // 格式化图片为 png 格式
102 | * 'save_as': '/iamge/png/new.png', // 处理成功后的图片保存路径
103 | * 'notify_url': 'http://your.notify.url' // 异步任务完成后的回调地址
104 | * )
105 | * )
106 | * ), true);
107 | *```
108 | *
109 | *
110 | *
111 | * @return array|bool 若文件是图片则返回图片基本信息,如:`array('x-upyun-width' => 123, 'x-upyun-height' => 50, 'x-upyun-frames'
112 | * => 1, 'x-upyun-file-type' => 'JPEG')`,否则返回空数组。当使用异步预处理功能时,返回结果为布尔值,成功为 `true`。
113 | *
114 | * @throws \Exception 上传失败时,抛出异常
115 | */
116 | public function write($path, $content, $params = array(), $withAsyncProcess = false)
117 | {
118 | if (!$content) {
119 | throw new \Exception('write content can not be empty.');
120 | }
121 |
122 | $upload = new Uploader($this->config);
123 | $response = $upload->upload($path, $content, $params, $withAsyncProcess);
124 | if ($withAsyncProcess) {
125 | return $response;
126 | }
127 | return Util::getHeaderParams($response->getHeaders());
128 | }
129 |
130 | /**
131 | * 读取云存储文件/目录内容
132 | *
133 | * @param string $path 又拍云存储中的文件或者目录路径
134 | * @param resource $saveHandler 文件内容写入本地文件流。例如 `$saveHandler = fopen('/local/file', 'w')
135 | * `。当设置该参数时,将以文件流的方式,直接将又拍云中的文件写入本地的文件流,或其他可以写入的流
136 | * @param array $params 可选参数,读取目录内容时,需要设置三个参数: `X-List-Iter` 分页开始位置(第一页不需要设置),`X-List-Limit` 获取的文件数量(默认 100,最大
137 | * 10000),`X-List-Order` 结果以时间正序或者倒序
138 | *
139 | * @return mixed $return 当读取文件且没有设置 `$saveHandler` 参数时,返回一个字符串类型,表示文件内容;设置了 `$saveHandler` 参数时,返回布尔值
140 | * `true`。当读取目录时,返回一个数组,表示目录下的文件列表。目录下文件内容过多时,需要通过判断返回数组中的 `is_end` 属性,进行分页读取内容
141 | *
142 | * @throws \Exception
143 | */
144 | public function read($path, $saveHandler = null, $params = array())
145 | {
146 | $req = new Rest($this->config);
147 | $response = $req->request('GET', $path)
148 | ->withHeaders($params)
149 | ->send();
150 |
151 |
152 | $params = Util::getHeaderParams($response->getHeaders());
153 |
154 |
155 | if (! isset($params['x-upyun-list-iter'])) {
156 | if (is_resource($saveHandler)) {
157 | Psr7\copy_to_stream($response->getBody(), Psr7\stream_for($saveHandler));
158 | return true;
159 | } else {
160 | return $response->getBody()->getContents();
161 | }
162 | } else {
163 | $files = Util::parseDir($response->getBody()->getContents());
164 | return array('files' => $files, 'is_end' => $params['x-upyun-list-iter'] === 'g2gCZAAEbmV4dGQAA2VvZg', 'iter' => $params['x-upyun-list-iter']);
165 | }
166 | }
167 |
168 | /**
169 | * 判断文件是否存在于又拍云存储
170 | *
171 | * 注意: 对刚删除的文件, 立即调用该方法可能会返回 true, 因为服务端执行删除操作后可能会有很短暂的延迟.
172 | *
173 | * @param string $path 云存储的文件路径
174 | *
175 | * @return bool 存在时返回 `true`,否则返回 `false`
176 | * @throws \Exception
177 | */
178 | public function has($path)
179 | {
180 | $req = new Rest($this->config);
181 | try {
182 | $req->request('HEAD', $path)
183 | ->send();
184 | } catch (GuzzleHttp\Exception\BadResponseException $e) {
185 | $statusCode = $e->getResponse()->getStatusCode();
186 | if ($statusCode === 404) {
187 | return false;
188 | } else {
189 | throw $e;
190 | }
191 | }
192 |
193 | return true;
194 | }
195 |
196 | /**
197 | * 获取云存储文件/目录的基本信息
198 | *
199 | * @param string $path 云存储的文件路径
200 | * @param array $otherHeaders 设置了后,方法将返回其他 http header 中的信息,默认为空
201 | *
202 | * @return array 返回一个数组,默认包含以下 key
203 | * - `x-upyun-file-type` 当 $path 是目录时,值为 *folder*,当 $path 是文件时,值为 *file*,
204 | * - `x-upyun-file-size` 文件大小
205 | * - `x-upyun-file-date` 文件的创建时间
206 | */
207 | public function info($path, $otherHeaders = array())
208 | {
209 | $req = new Rest($this->config);
210 | $response = $req->request('HEAD', $path)
211 | ->send();
212 | return Util::getHeaderParams($response->getHeaders(), $otherHeaders);
213 | }
214 |
215 | /**
216 | * 获取文件的文档类型
217 | * @param string $path 云存储文件路径
218 | * @return string 文档类型,e.g: `appcation/json`,获取失败返回空字符串
219 | */
220 | public function getMimetype($path)
221 | {
222 | $params = $this->info($path, array('content-type'));
223 | if (isset($params['content-type'])) {
224 | return explode(';', $params['content-type'])[0];
225 | }
226 | return '';
227 | }
228 |
229 | /**
230 | * 删除文件或者目录
231 | *
232 | * @param string $path 文件或目录在又拍云存储的路径
233 | * @param bool $async 是否异步删除,默认为 false,表示同步删除。当需要批量删除大量文件时,必须选择异步删除
234 | *
235 | * @return bool 删除成功返回 true,否则 false
236 | * @throws \Exception 删除不存在的文件将会抛出异常
237 | */
238 | public function delete($path, $async = false)
239 | {
240 | $req = new Rest($this->config);
241 | $req->request('DELETE', $path);
242 | if ($async) {
243 | $req->withHeader('x-upyun-async', 'true');
244 | }
245 | $res = $req->send();
246 | return $res->getStatusCode() === 200;
247 | }
248 |
249 | /**
250 | * 创建目录
251 | *
252 | * @param string $path 需要在又拍云存储创建的目录路径
253 | *
254 | * @return bool 创建成功返回 true,否则返回 false
255 | * @throws \Exception
256 | */
257 | public function createDir($path)
258 | {
259 | $path = rtrim($path, '/') . '/';
260 | $req = new Rest($this->config);
261 | $res = $req->request('POST', $path)
262 | ->withHeader('folder', 'true')
263 | ->send();
264 | return $res->getStatusCode() === 200;
265 | }
266 |
267 | /**
268 | * 删除文件或者目录
269 | *
270 | * @param string $path 需要被删除的云存储文件或目录路径
271 | *
272 | * @return bool 成功返回 true,否则 false
273 | * @throws \Exception
274 | */
275 | public function deleteDir($path)
276 | {
277 | return $this->delete($path);
278 | }
279 |
280 | /**
281 | * 获取目录下存储使用量
282 | *
283 | * @param string $path 云存储目录路径,默认为根目录,表示整个云存储服务使用的空间大小
284 | * @return string 存储使用量,单位字节
285 | * @throws \Exception
286 | */
287 | public function usage($path = '/')
288 | {
289 | $path = rtrim($path, '/') . '/';
290 | $req = new Rest($this->config);
291 | $response = $req->request('GET', $path . '?usage')
292 | ->send();
293 |
294 | return $response->getBody()->getContents();
295 | }
296 |
297 | /**
298 | * 复制文件。只能操作文件,不能操作文件夹。
299 | *
300 | * @param string $source 源文件地址
301 | * @param string $target 目标文件地址
302 | * @return bool 复制成功返回 true,否则 false
303 | * @throws \Exception
304 | */
305 | public function copy($source, $target)
306 | {
307 | $source = '/' . $this->config->serviceName . '/' . ltrim($source, '/');
308 | $req = new Rest($this->config);
309 | $response = $req->request('PUT', $target)
310 | ->withHeader('X-Upyun-Copy-Source', $source)
311 | ->send();
312 | return util::isSuccess($response->getStatusCode());
313 | }
314 |
315 | /**
316 | * 移动文件。可以进行文件重命名、文件移动,只能操作文件,不能操作文件夹。
317 | *
318 | * @param string $source 源文件地址
319 | * @param string $target 目标文件地址
320 | * @return bool 移动成功返回 true,否则 false
321 | * @throws \Exception
322 | */
323 | public function move($source, $target)
324 | {
325 | $source = '/' . $this->config->serviceName . '/' . ltrim($source, '/');
326 | $req = new Rest($this->config);
327 | $response = $req->request('PUT', $target)
328 | ->withHeader('X-Upyun-Move-Source', $source)
329 | ->send();
330 | return util::isSuccess($response->getStatusCode());
331 | }
332 |
333 | /**
334 | * 刷新缓存
335 | *
336 | * @param array|string $urls 需要刷新的文件 url 列表
337 | *
338 | * @return array 刷新失败的 url 列表,若全部刷新成功则为空数组
339 | */
340 | public function purge($urls)
341 | {
342 | $urlString = $urls;
343 | if (is_array($urls)) {
344 | $urlString = implode("\n", $urls);
345 | }
346 |
347 | $client = new Client([
348 | 'timeout' => $this->config->timeout
349 | ]);
350 | $response = $client->request('POST', Config::ED_PURGE, [
351 | 'headers' => Signature::getPurgeSignHeader($this->config, $urlString),
352 | 'form_params' => ['purge' => $urlString]
353 | ]);
354 | $result = json_decode($response->getBody()->getContents(), true);
355 | return $result['invalid_domain_of_url'];
356 | }
357 |
358 | /**
359 | * 异步云处理
360 | *
361 | * 该方法是基于[又拍云云处理](http://docs.upyun.com/cloud/) 服务实现,可以实现音视频的转码、切片、剪辑;文件的压缩解压缩;文件拉取功能
362 | *
363 | * 注意:
364 | * - 所有需要调用该方法处理的资源,必须已经上传到云存储服务
365 | * - 使用 `process` 之前,必须配置 `config->processNotifyUrl`,否则会提交任务失败
366 | *
367 | * 例如视频转码:
368 | * ```
369 | * process(array(
370 | * array(
371 | * 'type' => 'video', // video 表示视频任务, audio 表示音频任务
372 | * 'avopts' => '/s/240p(4:3)/as/1/r/30', // 处理参数,`s` 表示输出的分辨率,`r` 表示视频帧率,`as` 表示是否自动调整分辨率
373 | * 'save_as' => '/video/240/new.mp4', // 新视频在又拍云存储的保存路径
374 | * ),
375 | * ... // 同时还可以添加其他任务
376 | * ), Upyun::$PROCESS_TYPE_MEDIA, $source)
377 | * ```
378 | *
379 | * @param array $tasks 需要处理的任务
380 | * @param string $type 异步云处理任务类型,可选值:
381 | * - `Upyun::$PROCESS_TYPE_MEDIA` 异步音视频处理
382 | * - `Upyun::$PROCESS_TYPE_ZIP` 文件压缩
383 | * - `Upyun::$PROCESS_TYPE_UNZIP` 文件解压
384 | * - `Upyun::$PROCESS_TYPE_SYNC_FILE` 文件拉取
385 | * - `Upyun::$PROCESS_TYPE_STITCH` 图片拼接
386 | * @param string $source 可选参数,处理异步音视频任务时,需要传递该参数,表示需要处理的文件路径
387 | *
388 | * @return array 任务 ID,提交了多少任务,便会返回多少任务 ID,与提交任务的顺序保持一致。可以通过任务 ID 查询处理进度。格式如下:
389 | * ```
390 | * array(
391 | * '35f0148d414a688a275bf915ba7cebb2',
392 | * '98adbaa52b2f63d6d7f327a0ff223348',
393 | * )
394 | * ```
395 | * @throws \Exception
396 | */
397 | public function process($tasks, $type, $source = '')
398 | {
399 | $video = new Api\Pretreat($this->config);
400 |
401 | $options = array();
402 | switch($type) {
403 | case self::$PROCESS_TYPE_MEDIA:
404 | $options['accept'] = 'json';
405 | $options['source'] = $source;
406 | break;
407 | case self::$PROCESS_TYPE_ZIP:
408 | $options['app_name'] = 'compress';
409 | break;
410 | case self::$PROCESS_TYPE_UNZIP:
411 | $options['app_name'] = 'depress';
412 | break;
413 | case self::$PROCESS_TYPE_SYNC_FILE:
414 | $options['app_name'] = 'spiderman';
415 | break;
416 | case self::$PROCESS_TYPE_CONVERT:
417 | $options['app_name'] = 'uconvert';
418 | break;
419 | case self::$PROCESS_TYPE_STITCH:
420 | $options['app_name'] = 'jigsaw';
421 | break;
422 | default:
423 | throw new \Exception('upyun - not support process type.');
424 |
425 | }
426 | return $video->process($tasks, $options);
427 | }
428 |
429 | /**
430 | * 查询异步云处理任务进度
431 | *
432 | * 根据 `process` 方法返回的任务 ID,通过该访问查询处理进度
433 | *
434 | * @param array $taskIds 任务 ID
435 | *
436 | * @return bool|array 查询失败返回布尔值 `false`,否则返回每个任务的百分比进度信息,格式如下:
437 | * ```
438 | * array(
439 | * '35f0148d414a688a275bf915ba7cebb2' => 100, // 100 表示任务完成
440 | * 'c3103189fa906a5354d29bd807e8dc51' => 35,
441 | * '98adbaa52b2f63d6d7f327a0ff223348' => null, // null 表示任务未开始,或异常
442 | * )
443 | * ```
444 | */
445 | public function queryProcessStatus($taskIds)
446 | {
447 | $video = new Api\Pretreat($this->config);
448 | return $video->query($taskIds, '/status/');
449 | }
450 |
451 | /**
452 | * 查询异步云处理任务结果
453 | *
454 | * 根据 `process` 方法返回的任务 ID,通过该访问查询处理结果,会包含每个任务详细信息
455 | * @param array $taskIds 任务 ID
456 | *
457 | * @return bool|mixed 查询失败返回 `false`,否则返回每个任务的处理结果,格式如下:
458 | * ```
459 | * array(
460 | * '9d9c32b63a1034834e77672c6f51f661' => array(
461 | * 'path' => array('/v2.mp4'),
462 | * 'signature' => '4042c1f07f546d28',
463 | * 'status_code' => 200,
464 | * 'service_name' => 'your_storage_service',
465 | * 'description' => 'OK',
466 | * 'task_id' => '9d9c32b63a1034834e77672c6f51f661',
467 | * 'timestamp' => 1472010684
468 | * )
469 | * )
470 | * ```
471 | */
472 | public function queryProcessResult($taskIds)
473 | {
474 | $video = new Api\Pretreat($this->config);
475 | return $video->query($taskIds, '/result/');
476 | }
477 |
478 | /**
479 | * 多个 m3u8 文件拼接
480 | * @param array $files 保存在又拍云云存储中的多个 m3u8 文件路径
481 | * @param string $saveAs 拼接生成的新 m3u8 文件保存路径
482 | *
483 | * @return array 见 [m3u8 拼接 - 响应](http://docs.upyun.com/cloud/sync_video/#_3)
484 | */
485 | public function m3u8Concat($files, $saveAs)
486 | {
487 | $p = new Api\SyncVideo($this->config);
488 | return $p->process([
489 | 'm3u8s' => $files,
490 | 'save_as' => $saveAs,
491 | ], '/m3u8er/concat');
492 | }
493 |
494 | /**
495 | * 单个 m3u8 文件剪辑
496 | * @param string $file 需要剪辑的又拍云云存储中的 m3u8 文件路径
497 | * @param string $saveAs 剪辑完成后新的 m3u8 文件保存路径
498 | * @param array $slice 需要被保留或删除的片段。
499 | * @param bool $isInclude 默认为 `true` 表示 `$slice` 参数描述的片段被保留,否则表示 `$slice` 参数描述的片段被删除
500 | * @param bool $index 指定 `$slice` 参数的格式,默认为 `false` 表示使用时间范围描述片段,单位秒:`[<开始时间>, <结束时间>]`;`true` 表示使用 `m3u8` 文件的分片序号,从 0 开始,这种方式可以一次对多个片段操作
501 | *
502 | * @return array 见 [m3u8 剪辑 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
503 | */
504 | public function m3u8Clip($file, $saveAs, $slice = array(), $isInclude = true, $index = false)
505 | {
506 | $p = new Api\SyncVideo($this->config);
507 | $params = [
508 | 'm3u8' => $file,
509 | 'save_as' => $saveAs,
510 | 'index' => $index,
511 | ];
512 | if ($isInclude) {
513 | $params['include'] = $slice;
514 | } else {
515 | $params['exclude'] = $slice;
516 | }
517 | return $p->process($params, '/m3u8er/clip');
518 | }
519 |
520 | /**
521 | * 获取单个 m3u8 文件描述信息
522 | * @param string $file 又拍云云存储的中的 m3u8 文件路径
523 | *
524 | * @return array 见 [获取 m3u8 信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
525 | */
526 | public function m3u8Meta($file)
527 | {
528 | $p = new Api\SyncVideo($this->config);
529 | return $p->process([
530 | 'm3u8' => $file,
531 | ], '/m3u8er/get_meta');
532 | }
533 |
534 | /**
535 | * 视频截图,可以对 mp4、m3u8 等视频文件进行截图
536 | * @param string $file 需要截图的又拍云云存储中的视频文件路径
537 | * @param string $saveAs 截图保存路径
538 | * @param string $point 截图时间点,`HH:MM:SS` 格式
539 | * @param string $size 截图尺寸 `宽x高` 格式的字符串。默认和视频尺寸一致
540 | * @param string $format 截图保存的格式,默认根据 `$saveAs` 参数的后缀生成,可以指定 `jpg | png | webp` 三种格式
541 | *
542 | * @return array 见 [视频截图 - 响应](http://docs.upyun.com/cloud/sync_video/#m3u8_2)
543 | */
544 | public function snapshot($file, $saveAs, $point, $size = '', $format = '')
545 | {
546 | $p = new Api\SyncVideo($this->config);
547 | $params = [
548 | 'source' => $file,
549 | 'save_as' => $saveAs,
550 | 'point' => $point,
551 | ];
552 | if ($size) {
553 | $params['size'] = $size;
554 | }
555 | if ($format) {
556 | $params['format'] = $format;
557 | }
558 | return $p->process($params, '/snapshot');
559 | }
560 |
561 | /**
562 | * 获取音视频文件元信息
563 | * @param string $file 又拍云云存储的中的音视频文件路径
564 | *
565 | * @return array 见 [获取音视频文件信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_16)
566 | */
567 | public function avMeta($file)
568 | {
569 | $p = new Api\SyncVideo($this->config);
570 | return $p->process([
571 | 'source' => $file,
572 | ], '/avmeta/get_meta');
573 | }
574 | }
575 |
--------------------------------------------------------------------------------
/src/Upyun/Util.php:
--------------------------------------------------------------------------------
1 | $value) {
20 | $header = strtolower($header);
21 | if (strpos($header, 'x-upyun-') !== false) {
22 | $params[$header] = $value[0];
23 | } else if (in_array($header, $otherParams)) {
24 | $params[$header] = $value[0];
25 | }
26 | }
27 | return $params;
28 | }
29 |
30 | public static function parseDir($body)
31 | {
32 | $files = array();
33 | if (!$body) {
34 | return array();
35 | }
36 |
37 | $lines = explode("\n", $body);
38 | foreach ($lines as $line) {
39 | $file = [];
40 | list($file['name'], $file['type'], $file['size'], $file['time']) = explode("\t", $line, 4);
41 | $files[] = $file;
42 | }
43 |
44 | return $files;
45 | }
46 |
47 | public static function base64Json($params)
48 | {
49 | return base64_encode(json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
50 | }
51 |
52 | public static function stringifyHeaders($headers)
53 | {
54 | $return = array();
55 | foreach ($headers as $key => $value) {
56 | $return[] = "$key: $value";
57 | }
58 | return $return;
59 | }
60 |
61 | public static function md5Hash($resource)
62 | {
63 | rewind($resource);
64 | $ctx = hash_init('md5');
65 | hash_update_stream($ctx, $resource);
66 | $md5 = hash_final($ctx);
67 | return $md5;
68 | }
69 |
70 | /**
71 | * GuzzleHttp\Psr\Uri use `parse_url`,not good for utf-8,
72 | * So should `encodeURI` first, before `new Psr7\Request`
73 | * @see http://stackoverflow.com/questions/4929584/encodeuri-in-php
74 | */
75 | public static function encodeURI($url)
76 | {
77 | $unescaped = array(
78 | '%2D'=>'-','%5F'=>'_','%2E'=>'.','%21'=>'!', '%7E'=>'~',
79 | '%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')'
80 | );
81 | $reserved = array(
82 | '%3B'=>';','%2C'=>',','%2F'=>'/','%3F'=>'?','%3A'=>':',
83 | '%40'=>'@','%26'=>'&','%3D'=>'=','%2B'=>'+','%24'=>'$'
84 | );
85 | $score = array(
86 | '%23'=>'#'
87 | );
88 | return strtr(rawurlencode($url), array_merge($reserved, $unescaped, $score));
89 | }
90 |
91 | public static function isSuccess($code)
92 | {
93 | return $code >= 200 && $code < 300;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------