Repository: upyun/php-sdk
Branch: master
Commit: c9f824626552
Files: 34
Total size: 86.7 KB
Directory structure:
gitextract_hexs_lgu/
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .scrutinizer.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── RoboFile.php
├── composer.json
├── doc.md
├── examples/
│ ├── Ai.php
│ ├── Async-Process.php
│ ├── Pre-Process.php
│ ├── README.md
│ ├── User-Profile.php
│ ├── client-upload/
│ │ ├── Readme.md
│ │ ├── index.html
│ │ ├── normalize.css
│ │ └── policy.php
│ ├── list-all-file.php
│ └── sample/
│ └── sample.pptx
├── phpunit.xml
├── src/
│ └── Upyun/
│ ├── Api/
│ │ ├── Form.php
│ │ ├── Pretreat.php
│ │ ├── Rest.php
│ │ └── SyncVideo.php
│ ├── Config.php
│ ├── Signature.php
│ ├── Uploader.php
│ ├── Upyun.php
│ └── Util.php
└── tests/
├── SignatureTest.php
├── UpyunTest.php
└── bootstrap.php
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
root = true
[*.{php,html}]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
* text=auto
/examples export-ignore
/tests export-ignore
/doc.md export-ignore
/phpunit.xml export-ignore
================================================
FILE: .gitignore
================================================
.DS_Store
.idea
vendor
composer.lock
release*
.php_cs.cache
coverage.clover
================================================
FILE: .scrutinizer.yml
================================================
build:
environment:
php:
version: 5.5
dependencies:
before:
- composer install
tests:
override:
-
command: phpunit
coverage:
file: coverage.clover
format: clover
filter:
paths:
- 'src/*'
- 'tests/*'
tools:
php_code_sniffer:
config:
standard: PSR2
================================================
FILE: CHANGELOG.md
================================================
## 3.3.0
- 增加同步视频处理功能
## 3.0.0
- 重写 API 接口,不兼容 2.x 版本
- 集合分块、刷新、视频预处理功能
## 2.2.0
- 增加 composer 支持,特别感谢 [@totoleo](https://github.com/totoleo) 将 `upyun/sdk` 仓库源修改为 UPYUN 官方项目地址
- 移除不再推荐使用的 API:`rmDir deleteFile readDir getWritedFileInfo`),建议使用推荐方法替代
- note: `2.1.0` 版本之前已经被 [@totoleo](https://github.com/totoleo) 使用
## 2.0.0
- 使用1.0.x系列版本SDK的用户,注意原有部分方法已经不再推荐使用(`@deprecated`标注),但是出于兼容考虑目前任然保留,建议更新升级程序使用新版SDK提供的方法。
================================================
FILE: LICENSE
================================================
Copyright (c) 2016 UPYUN
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# 又拍云 SDK for PHPer
[](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)
又拍云 PHP SDK,封装了[又拍云功能丰富的开放 API](http://docs.upyun.com/api/) ,帮助开发者快速对接文件云端存储、图片音视频云处理、智能鉴黄等功能
- [功能列表](#list)
- [使用说明](#use-instructions)
- [安装](#install)
- [文档](#doc)
- [示例](#usage)
- [贡献代码](#contribute)
- [社区](#community)
- [许可证](#license)
## 功能列表
SDK 包含如下功能
- 基于 [rest api](http://docs.upyun.com/api/rest_api/)
- 文件上传下载、目录创建删除等云存储基本操作
- [断点续传](http://docs.upyun.com/api/rest_api/#_3)
- 基于 [form api](http://docs.upyun.com/api/form_api/)
- 文件客户端上传 见`examples/client-upload`
- 上传预处理操作
- [同步音频处理](http://docs.upyun.com/cloud/sync_audio/)
- [文档转换](http://docs.upyun.com/cloud/uconvert/)
- [异步图片音视频处理](http://docs.upyun.com/api/form_api/#_7)
- [异步图片智能鉴黄](http://docs.upyun.com/ai/audit/)
- [异步云处理](http://docs.upyun.com/cloud/)
- [视频音频](http://docs.upyun.com/cloud/av/)异步转码、切片、截图、水印、剪辑、拼接等功能
- [文件异步解压缩](http://docs.upyun.com/cloud/unzip/)
- [文件异步拉取](http://docs.upyun.com/cloud/spider/)
- [异步图片拼接](http://docs.upyun.com/cloud/async_image/)
- [同步视频处理](http://docs.upyun.com/cloud/sync_video/):m3u8 文件同步拼接剪辑、其他视频文件同步截图功能
- [缓存刷新](http://docs.upyun.com/api/purge/)
*功能列表中的异步操作,均可以设置异步回调通知地址,接收异步处理结果*
*如果需要测试回调功能,可以通过[又拍云回调服务](https://hooks.upyun.com/)创建一个临时回调地址*
## 使用说明
### 安装
#### PHP >= 5.5
1.使用 `composer` 安装
推荐使用该方法安装,成为优雅的 PHPer :fire:
建议使用速度很快的国内[全量镜像](https://pkg.phpcomposer.com/#how-to-use-packagist-mirror)([又拍云赞助](https://pkg.phpcomposer.com/#donation))
```
composer require upyun/sdk
```
2.如果不适应 `composer` 管理,可以直接下载[压缩包](https://github.com/upyun/php-sdk/releases)(注意需要下载 `php-sdk-版本号.zip` 格式的 zip 压缩包,不是 Source code 源码压缩包),解压后,项目中添加如下代码:
```
require_once '/path/to/php-sdk/vendor/autoload.php';
```
### 文档
详细文档见 [doc.md](doc.md)
### 示例
先初始化又拍云服务配置:
```php
require_once('vendor/autoload.php'); // 只针对使用 composer 安装
// require_once '/path/to/php-sdk/vendor/autoload.php'; // 针对压缩包安装
use Upyun\Upyun;
use Upyun\Config;
$serviceConfig = new Config('yourServiceName', 'yourOperatorName', 'yourOperatorPwd');
$client = new Upyun($serviceConfig);
```
#### 字符串写入又拍云服务器
```
$client->write('/save/path', 'file content');
```
#### 文件流写入又拍云服务器
```
$file = fopen('/local/path/file', 'r');
$client->write('/save/path', $file);
```
#### 使用并行式断点续传上传文件
```
$serviceConfig->setUploadType('BLOCK_PARALLEL');
$client = new Upyun($serviceConfig);
$file = fopen('/local/path/file', 'r');
$client->write('/save/path', $file);
```
#### 上传图片并转换格式为 `png`,详见[上传作图](http://docs.upyun.com/cloud/image/#_2)
```
$file = fopen('/local/path/image.jpg', 'r');
$client->write('/save/image.png', $file, array('x-gmkerl-thumb' => '/format/png'));
```
#### 下载文件并保存到本地
```
$saveLocal = fopen('/local/path/image.jpg', 'w');
// 第二个参数不传时,read 方法将直接返回文件内容
$client->read('/remote/server/image.png', $saveLocal);
```
## 贡献代码
1. Fork
2. 为新特性创建一个新的分支
3. 发送一个 pull request 到 master 分支
## 社区
- [问答社区](http://segmentfault.com/upyun)
- [微博](http://weibo.com/upaiyun)
## 许可证
UPYUN PHP-SDK 基于 MIT 开源协议
================================================
FILE: RoboFile.php
================================================
collectionBuilder();
$workingPath = __DIR__ . DIRECTORY_SEPARATOR . $collection->workDir("release");
$collection->taskExec("composer create-project {$package} {$name} {$version}")
->dir($workingPath)
->arg('--prefer-dist')
->arg('--no-dev')
->arg('-vvv')
->taskExec('composer dump-autoload --optimize')
->dir($workingPath . DIRECTORY_SEPARATOR . $name)
->arg('-vvv');
$collection->run();
$zipFile = "release/{$name}-{$version}.zip";
$this->_remove($zipFile);
$this->taskPack($zipFile)
->addDir("php-sdk", __DIR__ . "/release/php-sdk")
->run();
$this->_deleteDir("release/$name");
}
}
================================================
FILE: composer.json
================================================
{
"name": "upyun/sdk",
"description": "UPYUN sdk for php",
"keywords": ["UPYUN", "sdk"],
"type": "library",
"minimum-stability": "stable",
"homepage": "https://github.com/upyun/php-sdk/",
"license": "MIT",
"require": {
"php": ">=5.5.0",
"ext-curl": "*",
"guzzlehttp/guzzle": "~6.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"phpdocumentor/phpdocumentor": "^2.9",
"consolidation/robo": "^1.0"
},
"autoload": {
"psr-4": { "Upyun\\": "src/Upyun/" }
},
"autoload-dev": {
"psr-4": { "Upyun\\Tests\\": "tests/" }
},
"authors": [
{
"name": "lfeng",
"email": "bonevv@gmail.com"
},
{
"name": "lvtongda",
"email": "riyao.lyu@gmail.com"
},
{
"name": "totoleo",
"email": "totoleo@163.com"
},
{
"name": "sabakugaara",
"email": "senellise@gmail.com"
}
]
}
================================================
FILE: doc.md
================================================
# SDK 文档
又拍云 php sdk 中所有封装的接口,均通过 `Upyun\Upyun` 类封装,后续新版本也会保持
该类方法向下兼容,sdk 中其他类及其方法不保证兼容性。如果有使用疑问欢迎提 [issues](https://github.com/upyun/php-sdk/issues)
## 方法列表
* [Upyun](#upyun)
* [__construct](#__construct)
* [setConfig](#setconfig) 更新服务配置
* [write](#write) 上传一个文件到又拍云存储
* [read](#read) 读取云存储文件/目录内容
* [has](#has) 判断文件是否存在于又拍云存储
* [info](#info) 获取云存储文件/目录的基本信息
* [getMimetype](#getmimetype) 获取云存储文件类型
* [delete](#delete) 删除文件或者目录
* [createDir](#createdir) 创建目录
* [deleteDir](#deletedir) 删除文件或者目录
* [usage](#usage) 获取目录下存储使用量
* [purge](#purge) 刷新缓存
* [process](#process) 异步云处理
* [queryProcessStatus](#queryprocessstatus) 查询异步云处理任务进度
* [queryProcessResult](#queryprocessresult) 查询异步云处理任务结果
## Upyun
`Upyun\Upyun` 类实现了又拍云云存储和云处理的所有接口,通过该类可以实现文件上传、下载;图片视频等多媒体资源云处理。
本文档中,提到的"服务"是指又拍云文件加速回又拍云源类型的服务(即原先的存储类服务)。
* 命名空间: `\Upyun\Upyun`
### __construct
Upyun constructor.
```php
Upyun::__construct( \Upyun\Config $config )
```
**参数列表:**
- **\Upyun\Config** `$config`
服务配置
---
### setConfig
更新服务配置
```php
Upyun::setConfig( \Upyun\Config $config )
```
当需要操作的新的服务时,使用该方法传入新的服务配置即可
**参数列表:**
- **\Upyun\Config** `$config`
服务配置
---
### write
上传一个文件到又拍云存储
```php
Upyun::write( string $path, string|resource $content, array $params = array(), boolean $withAsyncProcess = false )
```
上传的文件格式支持文件流或者字符串方式上传。除简单的文件上传外,针对多媒体资源(图片、音视频),还可以设置同步/异步预处理多媒体资源,例如:图片的裁剪缩放,音视频的转码截图等等众多又拍云强大的云处理功能
**参数列表:**
- **string** `$path`
被上传的文件在又拍云存储服务中保存的路径
- **string|resource** `$content`
被上传的文件内容(字符串),或者打开该文件获得的文件句柄(文件流)。当上传本地大文件时,推荐使用文件流的方式上传
- **array** `$params`
上传文件时,附加的自定义参数。支持 Content-MD5 Content-Type Content-Secret 等,详见 [上传参数](http://docs.upyun.com/api/rest_api/#_2),例如:
- 设置文件[保护秘钥](http://docs.upyun.com/api/rest_api/#Content-Secret) `write($path, $content, array('Content-Secret' => 'my-secret'))`;
- 添加[文件元信息](http://docs.upyun.com/api/rest_api/#metadata) `write($path, $content, array('X-Upyun-Meta-Foo' =>
'bar'))`
- [图片同步预处理](http://docs.upyun.com/cloud/image/#_5) `write($path, $content, array('x-gmkerl-thumb' => '/format/png'))`
- **boolean** `$withAsyncProcess`
默认为 `false`,当上传图片或者音视频资源时,可以设置该参数为 `true`,开启图片音视频的[异步处理功能](http://docs.upyun.com/api/form_api/#_6) ,例如:
```
// 以下参数会将新上传的图片,再异步生成另一份 png 格式的图片,原图不受影响
write($path, $content, array(
'apps' => array(
array(
'name' => 'thumb', //异步图片处理任务
'x-gmkerl-thumb' => '/format/png', // 格式化图片为 png 格式
'save_as': '/iamge/png/new.png', // 处理成功后的图片保存路径
'notify_url': 'http://your.notify.url' // 异步任务完成后的回调地址
)
)
), true);
```
**返回值:**
若文件是图片则返回图片基本信息,如:`array('x-upyun-width' => 123, 'x-upyun-height' => 50, 'x-upyun-frames'
=> 1, 'x-upyun-file-type' => 'JPEG')`,否则返回空数组。当使用异步预处理功能时,返回结果为布尔值,成功为 `true`。
---
### read
读取云存储文件/目录内容
```php
Upyun::read( string $path, resource $saveHandler = NULL, array $params = array() )
```
**参数列表:**
- **string** `$path`
又拍云存储中的文件或者目录路径
- **resource** `$saveHandler`
文件内容写入本地文件流。例如 `$saveHandler = fopen('/local/file', 'w')
`。当设置该参数时,将以文件流的方式,直接将又拍云中的文件写入本地的文件流,或其他可以写入的流
- **array** `$params`
可选参数,读取目录内容时,需要设置三个参数: `X-List-Iter` 分页开始位置(第一页不需要设置),`X-List-Limit` 获取的文件数量(默认 100,最大
10000),`X-List-Order` 结果以时间正序或者倒序
**返回值:**
$return 当读取文件且没有设置 `$saveHandler` 参数时,返回一个字符串类型,表示文件内容;设置了 `$saveHandler` 参数时,返回布尔值
`true`。当读取目录时,返回一个数组,表示目录下的文件列表。目录下文件内容过多时,需要通过判断返回数组中的 `is_end` 属性,进行分页读取内容
---
### has
判断文件是否存在于又拍云存储
```php
Upyun::has( string $path )
```
注意: 对刚删除的文件, 立即调用该方法可能会返回 true, 因为服务端执行删除操作后可能会有很短暂的延迟.
**参数列表:**
- **string** `$path`
云存储的文件路径
**返回值:**
存在时返回 `true`,否则返回 `false`
---
### info
获取云存储文件/目录的基本信息
```php
Upyun::info( string $path, array $otherHeaders)
```
**参数列表:**
- **string** `$path`
云存储的文件路径
- **string** `$otherHeaders`
设置了后,方法将返回其他 http header 中的信息,默认为空
**返回值:**
返回一个数组,默认包含以下 key
- `x-upyun-file-type` 当 $path 是目录时,值为 *folder*,当 $path 是文件时,值为 *file*,
- `x-upyun-file-size` 文件大小
- `x-upyun-file-date` 文件的创建时间
---
### getMimetype
获取云存储文件的文档类型
```php
Upyun::getMimetype( string $path )
```
**参数列表:**
- **string** `$path`
云存储的文件路径
**返回值:**
文档类型,e.g: `appcation/json`,获取失败返回空字符串
---
### delete
删除文件或者目录
```php
Upyun::delete( string $path, boolean $async = false )
```
**参数列表:**
- **string** `$path`
文件或目录在又拍云存储的路径
- **boolean** `$async`
是否异步删除,默认为 false,表示同步删除。当需要批量删除大量文件时,必须选择异步删除
**返回值:**
删除成功返回 true,否则 false
---
### createDir
创建目录
```php
Upyun::createDir( string $path )
```
**参数列表:**
- **string** `$path`
需要在又拍云存储创建的目录路径
**返回值:**
创建成功返回 true,否则返回 false
---
### deleteDir
删除文件或者目录
```php
Upyun::deleteDir( string $path )
```
**参数列表:**
- **string** `$path`
需要被删除的云存储文件或目录路径
**返回值:**
成功返回 true,否则 false
---
### usage
获取目录下存储使用量
```php
Upyun::usage( string $path = '/' )
```
**参数列表:**
- **string** `$path`
云存储目录路径,默认为根目录,表示整个云存储服务使用的空间大小
**返回值:**
存储使用量,单位字节
---
### copy
复制文件。只能操作文件,不能操作文件夹。
```php
Upyun::copy( string $source, string $target )
```
**参数列表:**
- **string** `$source`
源文件地址
- **string** `$target`
目标文件地址
**返回值:**
复制成功返回 true,否则返回 false
---
### move
移动文件。可以进行文件重命名、文件移动,只能操作文件,不能操作文件夹。
```php
Upyun::move( string $source, string $target )
```
**参数列表:**
- **string** `$source`
需要移动的文件地址
- **string** `$target`
目标文件地址
**返回值:**
移动成功返回 true,否则返回 false
---
### purge
刷新缓存
```php
Upyun::purge( array|string $urls )
```
**参数列表:**
- **array|string** `$urls`
需要刷新的文件 url 列表
**返回值:**
刷新失败的 url 列表,若全部刷新成功则为空数组
---
### process
异步云处理
```php
Upyun::process( array $tasks, string $type, string $source )
```
该方法是基于[又拍云云处理](http://docs.upyun.com/cloud/) 服务实现,可以实现音视频的转码、切片、剪辑;文件的压缩解压缩;文件拉取功能
注意:
- 所有需要调用该方法处理的资源,必须已经上传到云存储服务
- 使用 `process` 之前,必须配置 `config->processNotifyUrl`,否则会提交任务失败
例如视频转码:
```
process(array(
array(
'type' => 'video', // video 表示视频任务, audio 表示音频任务
'avopts' => '/s/240p(4:3)/as/1/r/30', // 处理参数,`s` 表示输出的分辨率,`r` 表示视频帧率,`as` 表示是否自动调整分辨率
'save_as' => '/video/240/new.mp4', // 新视频在又拍云存储的保存路径
),
... // 同时还可以添加其他任务
), Upyun::$PROCESS_TYPE_MEDIA, $source)
```
注意,被处理的资源需要已经上传到又拍云云存储
**参数列表:**
- **array** `$tasks`
需要处理的任务
- **string** `$type`
异步云处理任务类型,可选值:
- `Upyun::$PROCESS_TYPE_MEDIA` 异步音视频处理
- `Upyun::$PROCESS_TYPE_ZIP` 文件压缩
- `Upyun::$PROCESS_TYPE_UNZIP` 文件解压
- `Upyun::$PROCESS_TYPE_SYNC_FILE` 文件拉取
- `Upyun::$PROCESS_TYPE_STITCH` 图片拼接
- **string** `$source`
可选参数,处理异步音视频任务时,需要传递该参数,表示需要处理的文件路径
**返回值:**
任务 ID,提交了多少任务,便会返回多少任务 ID,与提交任务的顺序保持一致。可以通过任务 ID 查询处理进度。格式如下:
```
array(
'35f0148d414a688a275bf915ba7cebb2',
'98adbaa52b2f63d6d7f327a0ff223348',
)
```
---
### queryProcessStatus
音视频预处理任务进度查询
```php
Upyun::queryProcessStatus( array $taskIds )
```
根据 `process` 方法返回的任务 ID,通过该访问查询处理进度
**参数列表:**
- **array** `$taskIds`
任务 ID
**返回值:**
查询失败返回布尔值 `false`,否则返回每个任务的百分比进度信息,格式如下:
```
array(
'35f0148d414a688a275bf915ba7cebb2' => 100, // 100 表示任务完成
'c3103189fa906a5354d29bd807e8dc51' => 35,
'98adbaa52b2f63d6d7f327a0ff223348' => null, // null 表示任务未开始,或异常
)
```
---
### queryProcessResult
音视频预处理任务结果查询
```php
Upyun::queryProcessResult( array $taskIds )
```
根据 `process` 方法返回的任务 ID,通过该访问查询处理结果,会包含每个任务详细信息
**参数列表:**
- **array** `$taskIds`
任务 ID
**返回值:**
查询失败返回 `false`,否则返回每个任务的处理结果,格式如下:
```
array(
'9d9c32b63a1034834e77672c6f51f661' => array(
'path' => array('/v2.mp4'),
'signature' => '4042c1f07f546d28',
'status_code' => 200,
'service' => 'your_storage_service',
'description' => 'OK',
'task_id' => '9d9c32b63a1034834e77672c6f51f661',
'timestamp' => 1472010684
)
)
```
---
--------
> This document was automatically generated from source code comments on 2017-02-06 using [phpDocumentor](http://www.phpdoc.org/) and [cvuorinen/phpdoc-markdown-public](https://github.com/cvuorinen/phpdoc-markdown-public)
================================================
FILE: examples/Ai.php
================================================
processNotifyUrl = NOTIFY_URL;
$client = new Pretreat($config);
$liveClient = new SyncVideo($config);
/**
* 异步内容识别通用接口
*/
function asyncAudit($tasks, $appName)
{
global $client;
$options = array('app_name' => $appName);
$resp = $client->process($tasks, $options);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 内容识别(有存储)-图片
* tasks参数与说明见:http://docs.upyun.com/ai/audit/
*/
function imageAsyncAudit()
{
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'source' => IMAGE_SAVE_KEY
));
asyncAudit($tasks, 'imgaudit');
}
/**
* 内容识别(有存储)-视频点播
* tasks参数与说明见:http://docs.upyun.com/ai/audit/
*/
function videoAsyncAudit()
{
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'source' => VIDEO_SAVE_KEY
));
asyncAudit($tasks, 'videoaudit');
}
/**
* 内容识别(有存储)-视频直播
* params参数与说明见:http://docs.upyun.com/ai/audit/
*/
function liveAudit()
{
global $liveClient;
// 使用时,按文档和个人需求填写params
$params = array(
'service' => SERVICE,
'source' => RTMP_SOURCE,
'save_as' => '/{year}/{mon}/{day}/{hour}_{min}_{sec}.jpg',
'notify_url' => NOTIFY_URL,
);
$resp = $liveClient->process($params, '/liveaudit/create');
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 内容识别(有存储)-视频直播取消
* params参数与说明见:http://docs.upyun.com/ai/audit/
*/
function liveAuditCancel($taskID)
{
global $liveClient;
// 使用时,按文档和个人需求填写params
$params = array(
'service' => SERVICE,
'task_id' => $taskID,
);
$resp = $liveClient->process($params, '/liveaudit/cancel');
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 接口调用
*/
imageAsyncAudit();
videoAsyncAudit();
liveAudit();
liveAuditCancel('064ca517cb85e708796f33e378b9b4cd');
================================================
FILE: examples/Async-Process.php
================================================
processNotifyUrl = NOTIFY_URL;
$client = new Upyun($config);
/**
* 异步音视频处理
* tasks参数与说明见:http://docs.upyun.com/cloud/av/
*/
function videoAsyncProcess()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'type' => 'video',
'avopts' => '/s/128x96',
'save_as' => VIDEO_SAVE_AS,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_MEDIA, VIDEO_SAVE_KEY);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 压缩
* tasks参数与说明见:http://docs.upyun.com/cloud/unzip/
*/
function compress()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'sources' => array(IMAGE_SAVE_KEY, VIDEO_SAVE_KEY),
'save_as' => COMPRESS_SAVE,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_ZIP);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 解压缩
* tasks参数与说明见:http://docs.upyun.com/cloud/unzip/
*/
function depress()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'sources' => COMPRESS_SAVE,
'save_as' => REMOTE_DIR,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_UNZIP);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 文件拉取
* tasks参数与说明见:http://docs.upyun.com/cloud/spider/
*/
function spiderman()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'url' => URL,
'save_as' => SAVE_AS,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_SYNC_FILE);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 文档转换
* tasks参数与说明见:http://docs.upyun.com/cloud/uconvert/
*/
function fileAsyncConvert()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$tasks = array(
array(
'source' => DOC_SAVE_KEY,
'save_as' => DOC_SAVE_AS,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_CONVERT);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 异步图片拼接
* tasks参数与说明见:http://docs.upyun.com/cloud/async_image/
*/
function imageAsyncJoint()
{
global $client;
// 使用时,按文档和个人需求填写tasks
$imageMatrix = array(
array(
'/12/6.jpg',
'/12/6.jpg'
));
$tasks = array(
array(
'image_matrix' => $imageMatrix,
'save_as' => IMAGE_SAVE_AS,
));
$resp = $client->process($tasks, Upyun::$PROCESS_TYPE_STITCH);
echo json_encode($resp, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* 接口调用
*/
videoAsyncProcess();
compress();
depress();
spiderman();
fileAsyncConvert();
imageAsyncJoint();
================================================
FILE: examples/Pre-Process.php
================================================
NOTIFY_URL,
'apps' => $apps
);
echo $client->write($key, $fd, $params, true);
}
else
{
echo 'cannt open file:' . $file;
}
}
/**
* 图片异步上传预处理
* http://docs.upyun.com/cloud/image/
*/
function formImageAsyncProcess()
{
// 使用时,按文档和个人需求填写apps
$apps = array(array(
'name' => 'thumb',
'x-gmkerl-thumb' => '/format/png',
'save_as' => IMAGE_SAVE_AS,
));
formAsyncPreProcess(IMAGE_FILE, IMAGE_SAVE_KEY, $apps);
}
/**
* 图片同步上传预处理
* http://docs.upyun.com/cloud/image/
*/
function formImageSyncProcess()
{
global $client;
$fd = fopen(IMAGE_FILE, 'r');
if ($fd != NULL)
{
// 使用时,按文档和个人需求填写params
$params = array(
'notify-url' => NOTIFY_URL,
'x-gmkerl-thumb' => '/format/png',
);
echo $client->write(IMAGE_SAVE_KEY, $fd, $params, true);
}
else
{
echo 'cannt open file:' . $file;
}
}
/**
* 异步音视频上传预处理
* http://docs.upyun.com/cloud/av/
*/
function formVideoAsyncProcess()
{
// 使用时,按文档和个人需求填写apps
$apps = array(array(
'name' => 'naga',
'type' => 'video',
'avopts' => '/s/128x96',
'save_as' => VIDEO_SAVE_AS
));
formAsyncPreProcess(VIDEO_FILE, VIDEO_SAVE_KEY, $apps);
}
/**
* 文档转换上传预处理
* http://docs.upyun.com/cloud/uconvert/
*/
function formDocAsyncConvert()
{
// 使用时,按文档和个人需求填写apps
$apps = array(array(
'name' => 'uconvert',
'save_as' => DOC_SAVE_AS
));
formAsyncPreProcess(DOC_FILE, DOC_SAVE_KEY, $apps);
}
/**
* 图片内容识别上传预处理
* http://docs.upyun.com/ai/audit/
*/
function formImageAsyncAudit()
{
// 使用时,按文档和个人需求填写apps
$apps = array(array(
'name' => 'imgaudit',
));
formAsyncPreProcess(IMAGE_FILE, IMAGE_SAVE_KEY, $apps);
}
/**
* 视频内容识别上传预处理
* http://docs.upyun.com/ai/audit/
*/
function formVideoAsyncAudit()
{
// 使用时,按文档和个人需求填写apps
$apps = array(array(
'name' => 'videoaudit',
));
formAsyncPreProcess(VIDEO_FILE, VIDEO_SAVE_KEY, $apps);
}
/**
* 接口调用
*/
formImageAsyncProcess();
formImageSyncProcess();
formVideoAsyncProcess();
formDocAsyncConvert();
formImageAsyncAudit();
formVideoAsyncAudit();
================================================
FILE: examples/README.md
================================================
# php sdk examples
这些 examples 旨在帮助您快速了解使用又拍云 php-sdk。
这些 examples 都可以直接运行,但是在运行之前,需要根据您的需求填写相应的参数。
### 参数:
用户个人参数见 `User-Profile.php` 说明
又拍云服务参数,见又拍云文档
#### 举例说明:
身份信息
```
`USER_NAME` 您的操作员名
`SERVICE` 您的服务名
`PWD` 您的操作密码
`NOTIFY_URL` 通知 URL
```
================================================
FILE: examples/User-Profile.php
================================================
Document
================================================
FILE: examples/client-upload/normalize.css
================================================
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
================================================
FILE: examples/client-upload/policy.php
================================================
setFormApiKey('Mv83tlocuzkmfKKUFbz2s04FzTw=');
$data['save-key'] = $_GET['save_path'];
$data['expiration'] = time() + 120;
$data['bucket'] = BUCKET;
$policy = Util::base64Json($data);
$method = 'POST';
$uri = '/' . $data['bucket'];
$signature = Signature::getBodySignature($config, $method, $uri, null, $policy);
echo json_encode(array(
'policy' => $policy,
'authorization' => $signature
));
================================================
FILE: examples/list-all-file.php
================================================
read('/', null, array(
'X-List-Limit' => 100,
'X-List-Iter' => $start,
));
if (is_array($list['files'])) {
foreach ($list['files'] as $file) {
$total++;
if ($file['type'] === 'N') {
echo '文件名: ';
} else {
echo '目录名: ';
}
echo $file['name'];
echo ' 大小:' . $file['size'];
echo ' 修改时间:' . date('Y-m-d H:i:s', $file['time']);
echo "\n";
}
}
$start = $list['iter'];
} while (!$list['is_end']);
echo '总共存有文件 ' . $total . ' 个';
================================================
FILE: phpunit.xml
================================================
./tests/SignatureTest.php
./tests/UpyunTest.php
./src/
================================================
FILE: src/Upyun/Api/Form.php
================================================
config->serviceName;
if (!isset($params['expiration'])) {
$params['expiration'] = time() + 30 * 60 * 60; // 30 分钟
}
$policy = Util::base64Json($params);
$method = 'POST';
$signature = Signature::getBodySignature($this->config, $method, '/' . $params['service'], null, $policy);
$client = new Client([
'timeout' => $this->config->timeout,
]);
$response = $client->request($method, $this->endpoint, array(
'multipart' => array(
array(
'name' => 'policy',
'contents' => $policy,
),
array(
'name' => 'authorization',
'contents' => $signature,
),
array(
'name' => 'file',
'contents' => $stream,
)
)
));
return $response->getStatusCode() === 200;
}
}
================================================
FILE: src/Upyun/Api/Pretreat.php
================================================
processNotifyUrl) {
throw new \Exception("should config prosessNotifyUrl first.");
}
$this->config = $config;
}
public function process($tasks, $optionalParams = array())
{
$encodedTasks = Util::base64Json($tasks);
$client = new Client([
'timeout' => $this->config->timeout,
]);
$params = array(
'service' => $this->config->serviceName,
'notify_url' => $this->config->processNotifyUrl,
'tasks' => $encodedTasks,
);
$params = array_merge($params, $optionalParams);
$path = '/pretreatment/';
$method = 'POST';
$signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
$url = $this->config->getPretreatEndPoint() . $path;
$response = $client->request($method, $url, [
'headers' => $signedHeaders,
'form_params' => $params
]);
$body = $response->getBody()->getContents();
return json_decode($body, true);
}
public function query($taskIds, $path)
{
$client = new Client([
'timeout' => $this->config->timeout,
]);
$params = array(
'service' => $this->config->serviceName,
'task_ids' => implode(',', $taskIds)
);
$path = $path . '?' . http_build_query($params);
$method = 'GET';
$url = $this->config->getPretreatEndPoint() . $path;
$signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
$response = $client->request($method, $url, [
'headers' => $signedHeaders
]);
if ($response->getStatusCode() === 200) {
$body = $response->getBody()->getContents();
$result = json_decode($body, true);
if (is_array($result)) {
return $result['tasks'];
}
}
return false;
}
}
================================================
FILE: src/Upyun/Api/Rest.php
================================================
config = $config;
$this->endpoint = $config->getProtocol() . Config::$restApiEndPoint . '/' . $config->serviceName;
}
public function request($method, $storagePath)
{
$this->method = strtoupper($method);
$this->storagePath = '/' . ltrim($storagePath, '/');
return $this;
}
/**
* @param string|resource $file
*
* @return $this
*/
public function withFile($file)
{
$stream = Psr7\stream_for($file);
$this->file = $stream;
return $this;
}
/**
* @return mixed|\Psr\Http\Message\ResponseInterface
*/
public function send()
{
$client = new Client([
'timeout' => $this->config->timeout,
]);
$url = $this->endpoint . $this->storagePath;
$body = null;
if ($this->file && $this->method === 'PUT') {
$body = $this->file;
}
$request = new Psr7\Request(
$this->method,
Util::encodeURI($url),
$this->headers,
$body
);
$authHeader = Signature::getHeaderSign($this->config,
$this->method,
$request->getUri()->getPath()
);
foreach ($authHeader as $head => $value) {
$request = $request->withHeader($head, $value);
}
$response = $client->send($request, [
'debug' => $this->config->debug
]);
return $response;
}
public function withHeader($header, $value)
{
$header = strtolower(trim($header));
$this->headers[$header] = $value;
return $this;
}
public function withHeaders($headers)
{
if (is_array($headers)) {
foreach ($headers as $header => $value) {
$this->withHeader($header, $value);
}
}
return $this;
}
public function toRequest()
{
$url = $this->endpoint . $this->storagePath;
$body = null;
if ($this->file && $this->method === 'PUT') {
$body = $this->file;
}
$request = new Psr7\Request(
$this->method,
Util::encodeURI($url),
$this->headers,
$body
);
$authHeader = Signature::getHeaderSign($this->config,
$this->method,
$request->getUri()->getPath()
);
foreach ($authHeader as $head => $value) {
$request = $request->withHeader($head, $value);
}
return $request;
}
}
================================================
FILE: src/Upyun/Api/SyncVideo.php
================================================
config = $config;
}
public function process($params, $path) {
$client = new Client([
'timeout' => $this->config->timeout,
]);
$path = '/' . $this->config->serviceName . $path;
$method = 'POST';
$signedHeaders = Signature::getHeaderSign($this->config, $method, $path);
$url = $this->config->getSyncVideoEndPoint() . $path;
$response = $client->request($method, $url, [
'headers' => $signedHeaders,
'json' => $params
]);
$body = $response->getBody()->getContents();
return json_decode($body, true);
}
}
================================================
FILE: src/Upyun/Config.php
================================================
serviceName = $serviceName;
$this->bucketName = $serviceName;
$this->operatorName = $operatorName;
$this->setOperatorPassword($operatorPassword);
$this->useSsl = false;
self::$restApiEndPoint = self::ED_AUTO;
}
public function setOperatorPassword($operatorPassword)
{
$this->operatorPassword = md5($operatorPassword);
}
public function getFormApiKey()
{
if (! $this->formApiKey) {
throw new \Exception('form api key is empty.');
}
return $this->formApiKey;
}
public function setFormApiKey($key)
{
$this->formApiKey = $key;
}
public function getVersion()
{
return $this->version;
}
public function getPretreatEndPoint()
{
return $this->getProtocol() . self::ED_VIDEO;
}
public function getSyncVideoEndPoint()
{
return $this->getProtocol() . self::ED_SYNC_VIDEO;
}
public function getProtocol()
{
return $this->useSsl ? 'https://' : 'http://';
}
public function setUploadType($uploadType)
{
$this->uploadType = $uploadType;
}
public function setConcurrency($concurrency)
{
$this->concurrency = $concurrency;
}
}
================================================
FILE: src/Upyun/Signature.php
================================================
$sign,
'Date' => $gmtDate,
'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion()
);
return $headers;
}
/**
* 获取请求缓存刷新接口需要的签名头
*
* @param Config $serviceConfig
* @param $urlString
*
* @return array
*/
public static function getPurgeSignHeader(Config $serviceConfig, $urlString)
{
$gmtDate = gmdate('D, d M Y H:i:s \G\M\T');
$sign = md5("$urlString&{$serviceConfig->serviceName}&$gmtDate&{$serviceConfig->operatorPassword}");
return array(
'Authorization' => "UpYun {$serviceConfig->serviceName}:{$serviceConfig->operatorName}:$sign",
'Date' => $gmtDate,
'User-agent' => 'Php-Sdk/' . $serviceConfig->getVersion() . ' (purge api)'
);
}
/**
* 获取表单 API 需要的签名,依据 body 签名规则计算
* @param Config $serviceConfig
* @param $method 请求方法
* @param $uri 请求路径
* @param $date 请求时间
* @param $policy
* @param $contentMd5 请求 body 的 md5
*
* @return array
*/
public static function getBodySignature(Config $serviceConfig, $method, $uri, $date = null, $policy = null, $contentMd5 = null)
{
$data = array(
$method,
$uri
);
if ($date) {
$data[] = $date;
}
if ($policy) {
$data[] = $policy;
}
if ($contentMd5) {
$data[] = $contentMd5;
}
$signature = base64_encode(hash_hmac('sha1', implode('&', $data), $serviceConfig->operatorPassword, true));
return 'UPYUN ' . $serviceConfig->operatorName . ':' . $signature;
}
}
================================================
FILE: src/Upyun/Uploader.php
================================================
config = $config;
}
public function upload($path, $file, $params, $withAsyncProcess)
{
$stream = Psr7\stream_for($file);
$size = $stream->getSize();
$useBlock = $this->needUseBlock($size);
if ($withAsyncProcess) {
$req = new Form($this->config);
return $req->upload($path, $stream, $params);
}
if (! $useBlock) {
$req = new Rest($this->config);
return $req->request('PUT', $path)
->withHeaders($params)
->withFile($stream)
->send();
} elseif ($this->config->uploadType === 'BLOCK_PARALLEL') {
return $this->concurrentPointUpload($path, $stream, $params);
} else {
return $this->pointUpload($path, $stream, $params);
}
}
/**
* 串行式断点续传
* @param $path
* @param $stream
* @param $params
*
* @return mixed|\Psr\Http\Message\ResponseInterface
* @throws \Exception
*/
private function pointUpload($path, $stream, $params)
{
$req = new Rest($this->config);
$headers = array();
if (is_array($params)) {
foreach ($params as $key => $val) {
$headers['X-Upyun-Meta-' . $key] = $val;
}
}
$res = $req->request('PUT', $path)
->withHeaders(array_merge(array(
'X-Upyun-Multi-Stage' => 'initiate',
'X-Upyun-Multi-Type' => Psr7\mimetype_from_filename($path),
'X-Upyun-Multi-Length' => $stream->getSize(),
), $headers))
->send();
if ($res->getStatusCode() !== 204) {
throw new \Exception('init request failed when poinit upload!');
}
$init = Util::getHeaderParams($res->getHeaders());
$uuid = $init['x-upyun-multi-uuid'];
$blockSize = 1024 * 1024;
$partId = 0;
do {
$fileBlock = $stream->read($blockSize);
$res = $req->request('PUT', $path)
->withHeaders(array(
'X-Upyun-Multi-Stage' => 'upload',
'X-Upyun-Multi-Uuid' => $uuid,
'X-Upyun-Part-Id' => $partId
))
->withFile(Psr7\stream_for($fileBlock))
->send();
if ($res->getStatusCode() !== 204) {
throw new \Exception('upload request failed when poinit upload!');
}
$data = Util::getHeaderParams($res->getHeaders());
$partId = $data['x-upyun-next-part-id'];
} while ($partId != -1);
$res = $req->request('PUT', $path)
->withHeaders(array(
'X-Upyun-Multi-Uuid' => $uuid,
'X-Upyun-Multi-Stage' => 'complete'
))
->send();
if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
throw new \Exception('end request failed when poinit upload!');
}
return $res;
}
private function needUseBlock($fileSize)
{
if ($this->config->uploadType === 'BLOCK' ||
$this->config->uploadType === 'BLOCK_PARALLEL') {
return true;
} elseif ($this->config->uploadType === 'AUTO' &&
$fileSize >= $this->config->sizeBoundary) {
return true;
} else {
return false;
}
}
/**
* 并行式断点续传
* @param $path
* @param $stream
* @param $params
*
* @return mixed|\Psr\Http\Message\ResponseInterface
* @throws \Exception
*/
private function concurrentPointUpload($path, $stream, $params)
{
$req = new Rest($this->config);
$headers = array();
if (is_array($params)) {
foreach ($params as $key => $val) {
$headers['X-Upyun-Meta-' . $key] = $val;
}
}
$res = $req->request('PUT', $path)
->withHeaders(array_merge(array(
'X-Upyun-Multi-Disorder' => 'true',
'X-Upyun-Multi-Stage' => 'initiate',
'X-Upyun-Multi-Type' => Psr7\mimetype_from_filename($path),
'X-Upyun-Multi-Length' => $stream->getSize(),
), $headers))
->send();
if ($res->getStatusCode() !== 204) {
throw new \Exception('init request failed when poinit upload!');
}
$init = Util::getHeaderParams($res->getHeaders());
$uuid = $init['x-upyun-multi-uuid'];
$requests = function ($req, $path, $stream, $uuid) {
$blockSize = 1024 * 1024;
$total = ceil($stream->getSize() / $blockSize);
for ($i = 0; $i < $total; $i++) {
$fileBlock = $stream->read($blockSize);
yield $req->request('PUT', $path)
->withHeaders(array(
'X-Upyun-Multi-Stage' => 'upload',
'X-Upyun-Multi-Uuid' => $uuid,
'X-Upyun-Part-Id' => $i
))
->withFile(Psr7\stream_for($fileBlock))
->toRequest();
}
};
$client = new Client([
'timeout' => $this->config->timeout,
]);
$pool = new Pool($client, $requests($req, $path, $stream, $uuid), [
'concurrency' => $this->config->concurrency,
'fulfilled' => function ($res) {
if ($res->getStatusCode() !== 204) {
throw new \Exception('upload request failed when poinit upload!');
}
},
'rejected' => function () {
throw new \Exception('upload request failed when poinit upload!');
},
]);
$promise = $pool->promise();
$promise->wait();
$res = $req->request('PUT', $path)
->withHeaders(array(
'X-Upyun-Multi-Uuid' => $uuid,
'X-Upyun-Multi-Stage' => 'complete'
))
->send();
if ($res->getStatusCode() != 204 && $res->getStatusCode() != 201) {
throw new \Exception('end request failed when poinit upload!');
}
return $res;
}
}
================================================
FILE: src/Upyun/Upyun.php
================================================
setConfig($config);
}
/**
* 配置服务信息
*
* 当需要操作的新的服务时,使用该方法传入新的服务配置即可
*
* @param Config $config 服务配置
*
* @return $this
*/
public function setConfig(Config $config)
{
$this->config = $config;
return $this;
}
/**
* 上传一个文件到又拍云存储
*
* 上传的文件格式支持文件流或者字符串方式上传。除简单的文件上传外,针对多媒体资源(图片、音视频),还可以设置同步/异步预处理多媒体资源,例如:图片的裁剪缩放,音视频的转码截图等等众多又拍云强大的云处理功能
*
* @param string $path 被上传的文件在又拍云存储服务中保存的路径
* @param string|resource $content 被上传的文件内容(字符串),或者打开该文件获得的文件句柄(文件流)。当上传本地大文件时,推荐使用文件流的方式上传
* @param array $params 上传文件时,附加的自定义参数。支持 Content-MD5 Content-Type Content-Secret 等,详见 [上传参数](http://docs.upyun.com/api/rest_api/#_2),例如:
* - 设置文件[保护秘钥](http://docs.upyun.com/api/rest_api/#Content-Secret) `write($path, $content, array('Content-Secret' => 'my-secret'))`;
* - 添加[文件元信息](http://docs.upyun.com/api/rest_api/#metadata) `write($path, $content, array('X-Upyun-Meta-Foo' =>
* 'bar'))`
* - [图片同步预处理](http://docs.upyun.com/cloud/image/#_5) `write($path, $content, array('x-gmkerl-thumb' => '/format/png'))`
* @param bool $withAsyncProcess 默认为 `false`,当上传图片或者音视频资源时,可以设置该参数为 `true`,开启图片音视频的[异步处理功能](http://docs.upyun.com/api/form_api/#_6) ,例如:
*```
* // 以下参数会将新上传的图片,再异步生成另一份 png 格式的图片,原图不受影响
* write($path, $content, array(
* 'apps' => array(
* array(
* 'name' => 'thumb', //异步图片处理任务
* 'x-gmkerl-thumb' => '/format/png', // 格式化图片为 png 格式
* 'save_as': '/iamge/png/new.png', // 处理成功后的图片保存路径
* 'notify_url': 'http://your.notify.url' // 异步任务完成后的回调地址
* )
* )
* ), true);
*```
*
*
*
* @return array|bool 若文件是图片则返回图片基本信息,如:`array('x-upyun-width' => 123, 'x-upyun-height' => 50, 'x-upyun-frames'
* => 1, 'x-upyun-file-type' => 'JPEG')`,否则返回空数组。当使用异步预处理功能时,返回结果为布尔值,成功为 `true`。
*
* @throws \Exception 上传失败时,抛出异常
*/
public function write($path, $content, $params = array(), $withAsyncProcess = false)
{
if (!$content) {
throw new \Exception('write content can not be empty.');
}
$upload = new Uploader($this->config);
$response = $upload->upload($path, $content, $params, $withAsyncProcess);
if ($withAsyncProcess) {
return $response;
}
return Util::getHeaderParams($response->getHeaders());
}
/**
* 读取云存储文件/目录内容
*
* @param string $path 又拍云存储中的文件或者目录路径
* @param resource $saveHandler 文件内容写入本地文件流。例如 `$saveHandler = fopen('/local/file', 'w')
* `。当设置该参数时,将以文件流的方式,直接将又拍云中的文件写入本地的文件流,或其他可以写入的流
* @param array $params 可选参数,读取目录内容时,需要设置三个参数: `X-List-Iter` 分页开始位置(第一页不需要设置),`X-List-Limit` 获取的文件数量(默认 100,最大
* 10000),`X-List-Order` 结果以时间正序或者倒序
*
* @return mixed $return 当读取文件且没有设置 `$saveHandler` 参数时,返回一个字符串类型,表示文件内容;设置了 `$saveHandler` 参数时,返回布尔值
* `true`。当读取目录时,返回一个数组,表示目录下的文件列表。目录下文件内容过多时,需要通过判断返回数组中的 `is_end` 属性,进行分页读取内容
*
* @throws \Exception
*/
public function read($path, $saveHandler = null, $params = array())
{
$req = new Rest($this->config);
$response = $req->request('GET', $path)
->withHeaders($params)
->send();
$params = Util::getHeaderParams($response->getHeaders());
if (! isset($params['x-upyun-list-iter'])) {
if (is_resource($saveHandler)) {
Psr7\copy_to_stream($response->getBody(), Psr7\stream_for($saveHandler));
return true;
} else {
return $response->getBody()->getContents();
}
} else {
$files = Util::parseDir($response->getBody()->getContents());
return array('files' => $files, 'is_end' => $params['x-upyun-list-iter'] === 'g2gCZAAEbmV4dGQAA2VvZg', 'iter' => $params['x-upyun-list-iter']);
}
}
/**
* 判断文件是否存在于又拍云存储
*
* 注意: 对刚删除的文件, 立即调用该方法可能会返回 true, 因为服务端执行删除操作后可能会有很短暂的延迟.
*
* @param string $path 云存储的文件路径
*
* @return bool 存在时返回 `true`,否则返回 `false`
* @throws \Exception
*/
public function has($path)
{
$req = new Rest($this->config);
try {
$req->request('HEAD', $path)
->send();
} catch (GuzzleHttp\Exception\BadResponseException $e) {
$statusCode = $e->getResponse()->getStatusCode();
if ($statusCode === 404) {
return false;
} else {
throw $e;
}
}
return true;
}
/**
* 获取云存储文件/目录的基本信息
*
* @param string $path 云存储的文件路径
* @param array $otherHeaders 设置了后,方法将返回其他 http header 中的信息,默认为空
*
* @return array 返回一个数组,默认包含以下 key
* - `x-upyun-file-type` 当 $path 是目录时,值为 *folder*,当 $path 是文件时,值为 *file*,
* - `x-upyun-file-size` 文件大小
* - `x-upyun-file-date` 文件的创建时间
*/
public function info($path, $otherHeaders = array())
{
$req = new Rest($this->config);
$response = $req->request('HEAD', $path)
->send();
return Util::getHeaderParams($response->getHeaders(), $otherHeaders);
}
/**
* 获取文件的文档类型
* @param string $path 云存储文件路径
* @return string 文档类型,e.g: `appcation/json`,获取失败返回空字符串
*/
public function getMimetype($path)
{
$params = $this->info($path, array('content-type'));
if (isset($params['content-type'])) {
return explode(';', $params['content-type'])[0];
}
return '';
}
/**
* 删除文件或者目录
*
* @param string $path 文件或目录在又拍云存储的路径
* @param bool $async 是否异步删除,默认为 false,表示同步删除。当需要批量删除大量文件时,必须选择异步删除
*
* @return bool 删除成功返回 true,否则 false
* @throws \Exception 删除不存在的文件将会抛出异常
*/
public function delete($path, $async = false)
{
$req = new Rest($this->config);
$req->request('DELETE', $path);
if ($async) {
$req->withHeader('x-upyun-async', 'true');
}
$res = $req->send();
return $res->getStatusCode() === 200;
}
/**
* 创建目录
*
* @param string $path 需要在又拍云存储创建的目录路径
*
* @return bool 创建成功返回 true,否则返回 false
* @throws \Exception
*/
public function createDir($path)
{
$path = rtrim($path, '/') . '/';
$req = new Rest($this->config);
$res = $req->request('POST', $path)
->withHeader('folder', 'true')
->send();
return $res->getStatusCode() === 200;
}
/**
* 删除文件或者目录
*
* @param string $path 需要被删除的云存储文件或目录路径
*
* @return bool 成功返回 true,否则 false
* @throws \Exception
*/
public function deleteDir($path)
{
return $this->delete($path);
}
/**
* 获取目录下存储使用量
*
* @param string $path 云存储目录路径,默认为根目录,表示整个云存储服务使用的空间大小
* @return string 存储使用量,单位字节
* @throws \Exception
*/
public function usage($path = '/')
{
$path = rtrim($path, '/') . '/';
$req = new Rest($this->config);
$response = $req->request('GET', $path . '?usage')
->send();
return $response->getBody()->getContents();
}
/**
* 复制文件。只能操作文件,不能操作文件夹。
*
* @param string $source 源文件地址
* @param string $target 目标文件地址
* @return bool 复制成功返回 true,否则 false
* @throws \Exception
*/
public function copy($source, $target)
{
$source = '/' . $this->config->serviceName . '/' . ltrim($source, '/');
$req = new Rest($this->config);
$response = $req->request('PUT', $target)
->withHeader('X-Upyun-Copy-Source', $source)
->send();
return util::isSuccess($response->getStatusCode());
}
/**
* 移动文件。可以进行文件重命名、文件移动,只能操作文件,不能操作文件夹。
*
* @param string $source 源文件地址
* @param string $target 目标文件地址
* @return bool 移动成功返回 true,否则 false
* @throws \Exception
*/
public function move($source, $target)
{
$source = '/' . $this->config->serviceName . '/' . ltrim($source, '/');
$req = new Rest($this->config);
$response = $req->request('PUT', $target)
->withHeader('X-Upyun-Move-Source', $source)
->send();
return util::isSuccess($response->getStatusCode());
}
/**
* 刷新缓存
*
* @param array|string $urls 需要刷新的文件 url 列表
*
* @return array 刷新失败的 url 列表,若全部刷新成功则为空数组
*/
public function purge($urls)
{
$urlString = $urls;
if (is_array($urls)) {
$urlString = implode("\n", $urls);
}
$client = new Client([
'timeout' => $this->config->timeout
]);
$response = $client->request('POST', Config::ED_PURGE, [
'headers' => Signature::getPurgeSignHeader($this->config, $urlString),
'form_params' => ['purge' => $urlString]
]);
$result = json_decode($response->getBody()->getContents(), true);
return $result['invalid_domain_of_url'];
}
/**
* 异步云处理
*
* 该方法是基于[又拍云云处理](http://docs.upyun.com/cloud/) 服务实现,可以实现音视频的转码、切片、剪辑;文件的压缩解压缩;文件拉取功能
*
* 注意:
* - 所有需要调用该方法处理的资源,必须已经上传到云存储服务
* - 使用 `process` 之前,必须配置 `config->processNotifyUrl`,否则会提交任务失败
*
* 例如视频转码:
* ```
* process(array(
* array(
* 'type' => 'video', // video 表示视频任务, audio 表示音频任务
* 'avopts' => '/s/240p(4:3)/as/1/r/30', // 处理参数,`s` 表示输出的分辨率,`r` 表示视频帧率,`as` 表示是否自动调整分辨率
* 'save_as' => '/video/240/new.mp4', // 新视频在又拍云存储的保存路径
* ),
* ... // 同时还可以添加其他任务
* ), Upyun::$PROCESS_TYPE_MEDIA, $source)
* ```
*
* @param array $tasks 需要处理的任务
* @param string $type 异步云处理任务类型,可选值:
* - `Upyun::$PROCESS_TYPE_MEDIA` 异步音视频处理
* - `Upyun::$PROCESS_TYPE_ZIP` 文件压缩
* - `Upyun::$PROCESS_TYPE_UNZIP` 文件解压
* - `Upyun::$PROCESS_TYPE_SYNC_FILE` 文件拉取
* - `Upyun::$PROCESS_TYPE_STITCH` 图片拼接
* @param string $source 可选参数,处理异步音视频任务时,需要传递该参数,表示需要处理的文件路径
*
* @return array 任务 ID,提交了多少任务,便会返回多少任务 ID,与提交任务的顺序保持一致。可以通过任务 ID 查询处理进度。格式如下:
* ```
* array(
* '35f0148d414a688a275bf915ba7cebb2',
* '98adbaa52b2f63d6d7f327a0ff223348',
* )
* ```
* @throws \Exception
*/
public function process($tasks, $type, $source = '')
{
$video = new Api\Pretreat($this->config);
$options = array();
switch($type) {
case self::$PROCESS_TYPE_MEDIA:
$options['accept'] = 'json';
$options['source'] = $source;
break;
case self::$PROCESS_TYPE_ZIP:
$options['app_name'] = 'compress';
break;
case self::$PROCESS_TYPE_UNZIP:
$options['app_name'] = 'depress';
break;
case self::$PROCESS_TYPE_SYNC_FILE:
$options['app_name'] = 'spiderman';
break;
case self::$PROCESS_TYPE_CONVERT:
$options['app_name'] = 'uconvert';
break;
case self::$PROCESS_TYPE_STITCH:
$options['app_name'] = 'jigsaw';
break;
default:
throw new \Exception('upyun - not support process type.');
}
return $video->process($tasks, $options);
}
/**
* 查询异步云处理任务进度
*
* 根据 `process` 方法返回的任务 ID,通过该访问查询处理进度
*
* @param array $taskIds 任务 ID
*
* @return bool|array 查询失败返回布尔值 `false`,否则返回每个任务的百分比进度信息,格式如下:
* ```
* array(
* '35f0148d414a688a275bf915ba7cebb2' => 100, // 100 表示任务完成
* 'c3103189fa906a5354d29bd807e8dc51' => 35,
* '98adbaa52b2f63d6d7f327a0ff223348' => null, // null 表示任务未开始,或异常
* )
* ```
*/
public function queryProcessStatus($taskIds)
{
$video = new Api\Pretreat($this->config);
return $video->query($taskIds, '/status/');
}
/**
* 查询异步云处理任务结果
*
* 根据 `process` 方法返回的任务 ID,通过该访问查询处理结果,会包含每个任务详细信息
* @param array $taskIds 任务 ID
*
* @return bool|mixed 查询失败返回 `false`,否则返回每个任务的处理结果,格式如下:
* ```
* array(
* '9d9c32b63a1034834e77672c6f51f661' => array(
* 'path' => array('/v2.mp4'),
* 'signature' => '4042c1f07f546d28',
* 'status_code' => 200,
* 'service_name' => 'your_storage_service',
* 'description' => 'OK',
* 'task_id' => '9d9c32b63a1034834e77672c6f51f661',
* 'timestamp' => 1472010684
* )
* )
* ```
*/
public function queryProcessResult($taskIds)
{
$video = new Api\Pretreat($this->config);
return $video->query($taskIds, '/result/');
}
/**
* 多个 m3u8 文件拼接
* @param array $files 保存在又拍云云存储中的多个 m3u8 文件路径
* @param string $saveAs 拼接生成的新 m3u8 文件保存路径
*
* @return array 见 [m3u8 拼接 - 响应](http://docs.upyun.com/cloud/sync_video/#_3)
*/
public function m3u8Concat($files, $saveAs)
{
$p = new Api\SyncVideo($this->config);
return $p->process([
'm3u8s' => $files,
'save_as' => $saveAs,
], '/m3u8er/concat');
}
/**
* 单个 m3u8 文件剪辑
* @param string $file 需要剪辑的又拍云云存储中的 m3u8 文件路径
* @param string $saveAs 剪辑完成后新的 m3u8 文件保存路径
* @param array $slice 需要被保留或删除的片段。
* @param bool $isInclude 默认为 `true` 表示 `$slice` 参数描述的片段被保留,否则表示 `$slice` 参数描述的片段被删除
* @param bool $index 指定 `$slice` 参数的格式,默认为 `false` 表示使用时间范围描述片段,单位秒:`[<开始时间>, <结束时间>]`;`true` 表示使用 `m3u8` 文件的分片序号,从 0 开始,这种方式可以一次对多个片段操作
*
* @return array 见 [m3u8 剪辑 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
*/
public function m3u8Clip($file, $saveAs, $slice = array(), $isInclude = true, $index = false)
{
$p = new Api\SyncVideo($this->config);
$params = [
'm3u8' => $file,
'save_as' => $saveAs,
'index' => $index,
];
if ($isInclude) {
$params['include'] = $slice;
} else {
$params['exclude'] = $slice;
}
return $p->process($params, '/m3u8er/clip');
}
/**
* 获取单个 m3u8 文件描述信息
* @param string $file 又拍云云存储的中的 m3u8 文件路径
*
* @return array 见 [获取 m3u8 信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_6)
*/
public function m3u8Meta($file)
{
$p = new Api\SyncVideo($this->config);
return $p->process([
'm3u8' => $file,
], '/m3u8er/get_meta');
}
/**
* 视频截图,可以对 mp4、m3u8 等视频文件进行截图
* @param string $file 需要截图的又拍云云存储中的视频文件路径
* @param string $saveAs 截图保存路径
* @param string $point 截图时间点,`HH:MM:SS` 格式
* @param string $size 截图尺寸 `宽x高` 格式的字符串。默认和视频尺寸一致
* @param string $format 截图保存的格式,默认根据 `$saveAs` 参数的后缀生成,可以指定 `jpg | png | webp` 三种格式
*
* @return array 见 [视频截图 - 响应](http://docs.upyun.com/cloud/sync_video/#m3u8_2)
*/
public function snapshot($file, $saveAs, $point, $size = '', $format = '')
{
$p = new Api\SyncVideo($this->config);
$params = [
'source' => $file,
'save_as' => $saveAs,
'point' => $point,
];
if ($size) {
$params['size'] = $size;
}
if ($format) {
$params['format'] = $format;
}
return $p->process($params, '/snapshot');
}
/**
* 获取音视频文件元信息
* @param string $file 又拍云云存储的中的音视频文件路径
*
* @return array 见 [获取音视频文件信息 - 响应](http://docs.upyun.com/cloud/sync_video/#_16)
*/
public function avMeta($file)
{
$p = new Api\SyncVideo($this->config);
return $p->process([
'source' => $file,
], '/avmeta/get_meta');
}
}
================================================
FILE: src/Upyun/Util.php
================================================
$value) {
$header = strtolower($header);
if (strpos($header, 'x-upyun-') !== false) {
$params[$header] = $value[0];
} else if (in_array($header, $otherParams)) {
$params[$header] = $value[0];
}
}
return $params;
}
public static function parseDir($body)
{
$files = array();
if (!$body) {
return array();
}
$lines = explode("\n", $body);
foreach ($lines as $line) {
$file = [];
list($file['name'], $file['type'], $file['size'], $file['time']) = explode("\t", $line, 4);
$files[] = $file;
}
return $files;
}
public static function base64Json($params)
{
return base64_encode(json_encode($params, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
public static function stringifyHeaders($headers)
{
$return = array();
foreach ($headers as $key => $value) {
$return[] = "$key: $value";
}
return $return;
}
public static function md5Hash($resource)
{
rewind($resource);
$ctx = hash_init('md5');
hash_update_stream($ctx, $resource);
$md5 = hash_final($ctx);
return $md5;
}
/**
* GuzzleHttp\Psr\Uri use `parse_url`,not good for utf-8,
* So should `encodeURI` first, before `new Psr7\Request`
* @see http://stackoverflow.com/questions/4929584/encodeuri-in-php
*/
public static function encodeURI($url)
{
$unescaped = array(
'%2D'=>'-','%5F'=>'_','%2E'=>'.','%21'=>'!', '%7E'=>'~',
'%2A'=>'*', '%27'=>"'", '%28'=>'(', '%29'=>')'
);
$reserved = array(
'%3B'=>';','%2C'=>',','%2F'=>'/','%3F'=>'?','%3A'=>':',
'%40'=>'@','%26'=>'&','%3D'=>'=','%2B'=>'+','%24'=>'$'
);
$score = array(
'%23'=>'#'
);
return strtr(rawurlencode($url), array_merge($reserved, $unescaped, $score));
}
public static function isSuccess($code)
{
return $code >= 200 && $code < 300;
}
}
================================================
FILE: tests/SignatureTest.php
================================================
config = new Config('bucket', 'operator', 'password');
}
public function testGetBodySignature()
{
$sign = Signature::getBodySignature($this->config, 'POST', '/bucket');
$this->assertEquals($sign, 'UPYUN operator:Xx3G6+DAvUyCL2Y2npSW/giTFI8=');
}
}
================================================
FILE: tests/UpyunTest.php
================================================
setFormApiKey('Mv83tlocuzkmfKKUFbz2s04FzTw=');
$config->processNotifyUrl = 'http://localhost:9999';
self::$upyun = new Upyun($config);
self::$tempFilePath = __DIR__ . '/assets/test.txt';
touch(self::$tempFilePath);
}
public static function tearDownAfterClass()
{
unlink(self::$tempFilePath);
}
public function testWriteString()
{
$filename = '/中文/测试 +.txt';
$content = 'test file content';
self::$upyun->write($filename, $content);
$size = getUpyunFileSize($filename);
$this->assertEquals($size, strlen($content));
}
public function testWriteStream()
{
$filename = 'test.jpeg';
$f = fopen(__DIR__ . '/assets/sample.jpeg', 'rb');
if (!$f) {
throw new \Exception('open test file failed!');
}
self::$upyun->write($filename, $f);
$size = getUpyunFileSize($filename);
$this->assertEquals($size, PIC_SIZE);
}
public function testWriteWithAsyncProcess()
{
$filename = 'test_async.jpeg';
$newFilename = 'test_async.png';
$f = fopen(__DIR__ . '/assets/sample.jpeg', 'rb');
if (!$f) {
throw new \Exception('open test file failed!');
}
$result = self::$upyun->write($filename, $f, array(
'apps' => array(
array(
'name' => 'thumb',
'x-gmkerl-thumb' => '/format/png/fw/50',
'save_as' => $newFilename,
)
)
), true);
$size = getUpyunFileSize($filename);
$this->assertEquals($size, PIC_SIZE);
$this->assertEquals($result, true);
}
public function testWriteWithException()
{
$fs = new Upyun(new Config(BUCKET, USER_NAME, 'error-password'));
try {
$fs->write('test.txt', 'test file content');
} catch (\Exception $e) {
return ;
}
throw new \Exception('should get sign error.');
}
/**
* @depends testWriteString
*/
public function testReadFile()
{
$name = 'test-read.txt';
$str = 'test file content 2';
self::$upyun->write($name, $str);
//读取内容写入字符串
$content = self::$upyun->read($name);
$this->assertEquals($content, $str);
//读取内容写入文件流
$this->assertTrue(self::$upyun->read($name, fopen(self::$tempFilePath, 'wb')));
$this->assertEquals($str, file_get_contents(self::$tempFilePath));
}
/**
* @depends testWriteString
* @depends testReadFile
*/
public function testDeleteFile()
{
self::$upyun->write('test-delete.txt', 'test file content 3');
sleep(5);
self::$upyun->delete('test-delete.txt');
try {
self::$upyun->read('test-delete.txt');
} catch (\Exception $e) {
return ;
}
throw new \Exception('delete file failed');
}
/**
* @expectedException \Exception
*/
public function testDeleteNotExistsFile()
{
self::$upyun->delete('not-exists-test.txt');
}
/**
*/
public function testHas()
{
$name = 'test-has.txt';
self::$upyun->write($name, 'test file content 4');
$this->assertEquals(self::$upyun->has($name), true);
sleep(5);
self::$upyun->delete($name);
sleep(5);
$this->assertEquals(self::$upyun->has($name), false);
}
/**
* @depends testWriteString
* @depends testDeleteFile
*/
public function testInfo()
{
self::$upyun->write('test-info.txt', 'test file content 4');
$info = self::$upyun->info('test-info.txt');
$this->assertEquals($info['x-upyun-file-type'], 'file');
$this->assertEquals($info['x-upyun-file-size'], 19);
}
/**
* @depends testInfo
*/
public function testGetMimetype()
{
$type = self::$upyun->getMimetype('test-info.txt');
$this->assertEquals($type, 'text/plain');
}
/**
*/
public function testCreateDir()
{
self::$upyun->createDir('/test-dir');
$this->assertEquals(self::$upyun->has('/test-dir'), true);
self::$upyun->createDir('/test-dir2/');
$this->assertEquals(self::$upyun->has('/test-dir2'), true);
}
public function testReadDir()
{
$list = self::$upyun->read('/test-dir2/');
$this->assertEquals($list['is_end'], true);
self::$upyun->write('/test-dir2/test.txt', 'test file content 5');
$list = self::$upyun->read('/test-dir2/');
$this->assertEquals($list['is_end'], true);
$this->assertEquals(count($list['files']), 1);
$file = $list['files'][0];
$this->assertEquals($file['name'], 'test.txt');
$this->assertEquals($file['type'], 'N');
$this->assertEquals($file['size'], 19);
}
/**
* @depends testCreateDir
*/
public function testDeleteDir()
{
$result = self::$upyun->createDir('/test-delete-dir');
$this->assertEquals($result, true);
sleep(5);
$result = self::$upyun->deleteDir('/test-delete-dir');
$this->assertEquals($result, true);
}
public function testUsage()
{
$size = self::$upyun->usage();
$this->assertTrue($size > 0);
}
/**
* @depends testWriteString
*/
public function testCopy()
{
$source = 'test-copy.txt';
$target = 'test-copy-target.txt';
self::$upyun->write($source, 'test file content 6');
sleep(5);
self::$upyun->copy($source, $target);
$this->assertEquals(self::$upyun->has($target), true);
}
/**
* @depends testWriteString
*/
public function testMove()
{
$source = 'test-move.txt';
$target = 'test-move-target.txt';
self::$upyun->write($source, 'test file content 7');
sleep(5);
self::$upyun->move($source, $target);
$this->assertEquals(self::$upyun->has($source), false);
$this->assertEquals(self::$upyun->has($target), true);
}
public function testPurge()
{
$urls = self::$upyun->purge(getFileUrl('test.txt'));
$this->assertTrue(empty($urls));
$invalidUrl = 'http://xxxx.b0.xxxxxxxx-upyun.com/test.txt';
$urls = self::$upyun->purge($invalidUrl);
$this->assertTrue(count($urls) === 1);
$this->assertTrue($urls[0] === $invalidUrl);
}
public function testProcess()
{
$source = 'php-sdk-sample.mp4';
self::$upyun->write($source, fopen(__DIR__ . '/assets/SampleVideo_640x360_1mb.mp4', 'r'));
$result = self::$upyun->process(array(
array('type' => 'video', 'avopts' => '/s/240p(4:3)/as/1/r/30', 'return_info' => true, 'save_as' => '/video/result.mp4')
), Upyun::$PROCESS_TYPE_MEDIA, $source);
$this->assertTrue(strlen($result[0]) === 32);
self::$taskId = $result[0];
// test zip
$result2 = self::$upyun->process(array(array(
'sources' => ['./php-sdk-sample.mp4'],
'save_as' => '/php-sdk-sample-mp4.zip'
)), Upyun::$PROCESS_TYPE_ZIP);
$this->assertTrue(strlen($result2[0]) === 32);
}
/**
* @depends testProcess
*/
public function testQueryProcessStatus()
{
sleep(5);
$status = self::$upyun->queryProcessStatus(array(self::$taskId));
$this->assertTrue(array_key_exists(self::$taskId, $status));
}
/**
* @depends testProcess
*/
public function testQueryProcessResult()
{
sleep(5);
$result = self::$upyun->queryProcessResult(array(self::$taskId));
$this->assertTrue($result[self::$taskId]['path'][0] === '/video/result.mp4');
$this->assertTrue($result[self::$taskId]['status_code'] === 200);
}
public function testAvMeta()
{
$source = 'php-sdk-sample.mp4';
self::$upyun->write($source, fopen(__DIR__ . '/assets/SampleVideo_640x360_1mb.mp4', 'r'));
$result = self::$upyun->avMeta('/php-sdk-sample.mp4');
$this->assertTrue(count($result) === 2);
$this->assertTrue($result['streams'][0]['type'] === 'video');
}
public function testSnapshot()
{
sleep(5);
$source = 'php-sdk-sample.mp4';
self::$upyun->write($source, fopen(__DIR__ . '/assets/SampleVideo_640x360_1mb.mp4', 'r'));
$result = self::$upyun->snapshot('/php-sdk-sample.mp4', '/snapshot.jpg', '00:00:01', '720x480', 'jpg');
$this->assertTrue($result['status_code'] === 200);
}
public function testParallelUpload()
{
$config = new Config(BUCKET, USER_NAME, PWD);
$config->setUploadType('BLOCK_PARALLEL');
$upyun = new Upyun($config);
$filename = 'test_parallel.jpeg';
$upyun->write($filename, fopen(__DIR__ . '/assets/sample.jpeg', 'rb'));
$size = getUpyunFileSize($filename);
$this->assertEquals($size, PIC_SIZE);
}
}
================================================
FILE: tests/bootstrap.php
================================================