Full Code of upyun/php-sdk for AI

master c9f824626552 cached
34 files
86.7 KB
27.8k tokens
121 symbols
1 requests
Download .txt
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
[![Build Status](https://scrutinizer-ci.com/g/upyun/php-sdk/badges/build.png?b=master)](https://scrutinizer-ci.com/g/upyun/php-sdk/build-status/master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/upyun/php-sdk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/upyun/php-sdk/?branch=master) [![Code Coverage](https://scrutinizer-ci.com/g/upyun/php-sdk/badges/coverage.png?b=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)


<a name="list"></a>
## 功能列表

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/)创建一个临时回调地址*

<a name="use-instructions"></a>
## 使用说明

<a name="install"></a>
### 安装

#### 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';
```
<a name="doc"></a>
### 文档

详细文档见 [doc.md](doc.md)

<a name="usage"></a>
### 示例

先初始化又拍云服务配置:

```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);
```

<a name="contribute"></a>
## 贡献代码
 1. Fork
 2. 为新特性创建一个新的分支
 3. 发送一个 pull request 到 master 分支

<a name="community"></a>
## 社区

 - [问答社区](http://segmentfault.com/upyun)
 - [微博](http://weibo.com/upaiyun)

<a name="license"></a>
## 许可证

UPYUN PHP-SDK 基于 MIT 开源协议

<http://www.opensource.org/licenses/MIT>



================================================
FILE: RoboFile.php
================================================
<?php
/**
 * This is project's console commands configuration for Robo task runner.
 *
 * @see http://robo.li/
 */
class RoboFile extends \Robo\Tasks
{
    /**
     * Creates release zip
     *
     * @param string $version
     * @see https://gist.github.com/Rarst/5a8a65478755539770df653c4575219a
     */
    public function release($version)
    {
        $package = 'upyun/sdk';
        $name = 'php-sdk';
        $collection = $this->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&#124;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 = &#039;/&#039; )
```

  


**参数列表:**


- **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&#124;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
================================================
<?php
/**
 * 又拍云 PHP-SDK 人工智能使用实例
 * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数
 */

require  __DIR__ . '/User-Profile.php';

use Upyun\Config;
use Upyun\Api\Pretreat;
use Upyun\Api\SyncVideo;

$config = new Config(SERVICE, USER_NAME, PWD);
$config->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
================================================
<?php
/**
 * 又拍云 PHP-SDK 异步云处理使用实例
 * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数
 */

require  __DIR__ . '/User-Profile.php';

use Upyun\Config;
use Upyun\Upyun;

$config = new Config(SERVICE, USER_NAME, PWD);
$config->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
================================================
<?php
/**
 * 又拍云 PHP-SDK 上传预处理使用实例
 * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数
 */

require  __DIR__ . '/User-Profile.php';

use Upyun\Config;
use Upyun\Upyun;

$config = new Config(SERVICE, USER_NAME, PWD);
$client = new Upyun($config);


/**
 * 通用form上传预处理
 */
function formAsyncPreProcess($file, $key, $apps=array())
{
    global $client;
    $fd = fopen($file, 'r');
    if ($fd != NULL)
    {
        // 使用时,按文档和个人需求填写params
        $params = array(
            'notify-url' => 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
================================================
<?php
/**
 * 又拍云 PHP-SDK examle 用户配置文件
 * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置,参数
 */

require dirname(__DIR__) . '/vendor/autoload.php';

/**
 * 用户操作员名
 */
define('USER_NAME', '');

/**
 * 用户服务名
 */
define('SERVICE', '');

/**
 * 用户密码
 */
define('PWD', '');

/**
 * 指定的通知URL
 */
define('NOTIFY_URL', '');

/**
 * 本地图片路径,适用于图片文件上传,预处理
 */
define('IMAGE_FILE', './sample/sample.jpg');

/**
 * 本地视频路径,适用于视频文件上传,预处理
 */
define('VIDEO_FILE', './sample/sample.mp4');

/**
 * 本地文档路径,包括PDF,PPT,WORD,EXCEL,适用于文档文件上传,预处理
 */
define('DOC_FILE', './sample/sample.pptx');

/**
 * 云存储中保存的图片文件路径,适用于图片相关上传,预处理,图片内容识别
 */
define('IMAGE_SAVE_KEY', '/save.png');

/**
 * 云存储中保存的视频文件路径,适用于视频相关上传,预处理,视频内容识别
 */
define('VIDEO_SAVE_KEY', '/save.mp4');

/**
 * 云存储中保存的文档文件路径,适用于文档相关上传,预处理,文档转换
 */
define('DOC_SAVE_KEY', '/save.pptx');

/**
 * 云存储中 save_as 参数指定的图片路径,适用于图片相关
 */
define('IMAGE_SAVE_AS', '/process/save.png');

/**
 * 云存储中 save_as 参数指定的视频路径,适用于视频相关
 */
define('VIDEO_SAVE_AS', '/process/save.mp4');

/**
 * 云存储中 save_as 参数指定的文档路径,适用于文档转换 
 */
define('DOC_SAVE_AS', '/process/save');

/**
 * 云存储中 save_as 参数指定的路径,适用于文件拉取服务 
 */
define('SAVE_AS', '/process/save.jpg');

/**
 * 云存储中 save_as 参数指定的压缩文件路径,适用于文件压缩,解压 
 */
define('COMPRESS_SAVE', '/process/save.zip');

/**
 * 云存储中目录,适用于文件解压 
 */
define('REMOTE_DIR', '/process');

/**
 * 文件URL,适用于文件拉取 
 */
define('URL', 'http://p07vpkunh.bkt.clouddn.com/aaaaa/image.png');

/**
 * RTMP源,适用于内容识别-直播
 */
define('RTMP_SOURCE', 'rtmp://live.hkstv.hk.lxdns.com/live/hks');

================================================
FILE: examples/client-upload/Readme.md
================================================
## 客户端上传

本示例展示了如何使用表单 API, 直接从客户端进行安全的文件上传, 这种方式不需要客户服务器进行中转, 节省了客户服务器流量, 并且支持 HTTP/HTTPS 两种协议

DEMO 使用 `sdkimg` 空间进行演示, 上传成功后, 访问路径为 `http://sdkimg.b0.upaiyun.com/` 拼接保存路径

#### 运行示例

- `cd examples/client-upload`
- `php -S localhost:9000`

打开浏览器访问 `http://localhost:9000`, 选则文件上传即可.
示例中使用 body 签名,避免 header 签名存在跨域失败的情况,因为 `Access-Control-Allow-Headers: *` 兼容性不好,是刚设定的协议[标准](https://github.com/whatwg/fetch)


================================================
FILE: examples/client-upload/index.html
================================================
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="normalize.css">
    <script src="//cdn.bootcss.com/jquery/3.1.1/jquery.min.js" type="text/javascript"></script>
    <style>
        form {
            margin: 30px 0;
        }
        .submit {
            display: inline-block;
            width: 100px;
            background: #02a3c6;
            border: none;
            color: #fff;
            line-height: 40px;
            text-align: center;
            cursor: pointer;
        }
    </style>
</head>
<body>

<form action="http://v0.api.upyun.com/sdkimg" id="demoForm" method="POST" enctype="multipart/form-data">
    <fieldset>
        <legend>Client Upload Demo</legend>
        <input name="file" type="file">
        <input type="button" value="submit" class="submit" id="upload">
    </fieldset>
</form>

<script>
    // 文件保存的路径
    var save_path = '/test/filename.txt';
    $('#upload').on('click', function() {
        // 获取 policy 和 secret
        $.getJSON('http://localhost:9000/policy.php?save_path=' + save_path, function (data) {
            var uploadData = new FormData($('#demoForm')[0]);
            uploadData.append('policy', data.policy);
            uploadData.append('authorization', data.authorization);
            $.ajax({
                url: 'http://v0.api.upyun.com/sdkimg',
                type: 'POST',
                data: uploadData,
                cache: false,
                processData: false,
                contentType: false,
            }).done(function(data, textStatus) {
                alert('upload success');
            }).fail(function(res, textStatus, error) {
                try {
                    var body = JSON.parse(res.responseText);
                    alert('error: ' + body.message);
                } catch(e) {
                    console.error(e);
                }
            });
        });
    });
</script>
</body>
</html>


================================================
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
================================================
<?php
require  __DIR__ . '/../../tests/bootstrap.php';

use Upyun\Config;
use Upyun\Signature;
use Upyun\Util;

$config = new Config(BUCKET, USER_NAME, PWD);
$config->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
================================================
<?php
require  __DIR__ . '/../tests/bootstrap.php';

use Upyun\Config;
use Upyun\Upyun;

$config = new Config(BUCKET, USER_NAME, PWD);
$upyun = new Upyun($config);

$start = null;
$total = 0;
do {
    $list = $upyun->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
================================================
<phpunit backupGlobals="true"
         backupStaticAttributes="false"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false"
         syntaxCheck="false"
         bootstrap="tests/bootstrap.php">
    <testsuites>
        <testsuite name="upyun test cases">
            <file>./tests/SignatureTest.php</file>
            <file>./tests/UpyunTest.php</file>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory suffix=".php">./src/</directory>
        </whitelist>
    </filter>
    <logging>
        <log type="coverage-clover" target="coverage.clover"/>
    </logging>
</phpunit>


================================================
FILE: src/Upyun/Api/Form.php
================================================
<?php

namespace Upyun\Api;

use Upyun\Signature;
use Upyun\Util;
use GuzzleHttp\Client;

class Form extends Rest
{
    public function upload($path, $stream, $params)
    {
        $params['save-key'] = $path;
        $params['service'] = $this->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
================================================
<?php
namespace Upyun\Api;

use GuzzleHttp\Client;
use Upyun\Config;
use Upyun\Signature;
use Upyun\Util;

class Pretreat
{

    /**
     * @var Config
     */
    protected $config;

    public function __construct(Config $config)
    {
        if (!$config->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
================================================
<?php

namespace Upyun\Api;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use Upyun\Config;
use Upyun\Signature;
use Upyun\Util;

class Rest
{
    /**
     * @var Config
     */
    protected $config;

    protected $endpoint;
    protected $method;
    protected $storagePath;
    public $headers = [];

    /**
     * @var Psr7\Stream
     */
    protected $file;


    public function __construct(Config $config)
    {
        $this->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
================================================
<?php
/**
 * 同步视频处理
 */

namespace Upyun\Api;

use GuzzleHttp\Client;
use Upyun\Config;
use Upyun\Signature;


class SyncVideo {
    /**
     * @var Config
     */
    protected $config;

    public function __construct(Config $config)
    {
        $this->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
================================================
<?php
namespace Upyun;

/**
 * Class Config
 *
 * @package Upyun
 */
class Config
{
    /**
     * @var string 服务名称,将会被弃用
     */
    public $bucketName;

    /**
     * @var string 服务名称
     */
    public $serviceName;
    /**
     * @var string 操作员名
     */
    public $operatorName;
    /**
     * @var string 操作员密码 md5 hash 值
     */
    public $operatorPassword;

    /**
     * @var bool 是否使用 https
     */
    public $useSsl;

    /**
     * @var string 上传使用的接口类型,可以设置为 `REST`:使用 rest api 上传,`AUTO` 根据文件大小自动判断,`BLOCK` 使用串行式断点续传,`BLOCK_PARALLEL` 使用并行式断点续传
     * 当上传小文件时,不推荐使用断点续传;上传时如果设置了异步预处理`withAsyncProcess=true`,将会使用表单 api 上传
     */
    public $uploadType = 'AUTO';

    /**
     * @var int 上传的接口类型设置为 `AUTO` 时,文件大小的边界值:小于该值时,使用 rest api,否则使用断点续传。 默认 30M
     */
    public $sizeBoundary = 31457280;

    /**
     * @var int 并行式断点续传的并发数
     */
    public $concurrency = 5;

    /**
     * @var int request timeout seconds
     */
    public $timeout = 60;


    /**
     * @var string 异步云处理的回调通知地址
     */
    public $processNotifyUrl;

    /**
     * @var boolean curl debug
     */
    public $debug = false;

    private $version = '3.0.0';



    /**
     * @var string 表单 api 的秘钥
     */
    private $formApiKey;

    /**
     * @var string rest api 和 form api 的接口地址
     */
    public static $restApiEndPoint;


    /**
     * rest api 和 form api 接口请求地址,详见:http://docs.upyun.com/api/rest_api/
     */
    const ED_AUTO            = 'v0.api.upyun.com';
    const ED_TELECOM         = 'v1.api.upyun.com';
    const ED_CNC             = 'v2.api.upyun.com';
    const ED_CTT             = 'v3.api.upyun.com';

    /**
     * 异步云处理接口地址
     */
    const ED_VIDEO           = 'p0.api.upyun.com';

    /**
     * 刷新接口地址
     */
    const ED_PURGE           = 'http://purge.upyun.com/purge/';

    /**
     * 同步视频处理接口地址
     */
    const ED_SYNC_VIDEO           = 'p1.api.upyun.com';

    public function __construct($serviceName, $operatorName, $operatorPassword)
    {
        $this->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
================================================
<?php
namespace Upyun;

/**
 * Class Signature
 * @package Upyun
 */
class Signature
{
    /**
     * 获取分块上传接口的签名
     */
    const SIGN_MULTIPART     = 1;
    /**
     * 生成视频处理接口的签名
     */
    const SIGN_VIDEO         = 2;
    /**
     * 生成视频处理接口的签名(不需要操作员时使用)
     */
    const SIGN_VIDEO_NO_OPERATOR   = 3;

    /**
     * 获取 Header 签名需要的请求头
     *
     * @param Config $serviceConfig
     * @param $method 请求方法
     * @param $path  请求路径
     * @param $contentMd5 文件内容 md5
     *
     * @return array
     */
    public static function getHeaderSign($serviceConfig, $method, $path, $contentMd5 = null)
    {
        $gmtDate = gmdate('D, d M Y H:i:s \G\M\T');

        $policy = null;
        $sign = self::getBodySignature($serviceConfig, $method, $path, $gmtDate, $policy, $contentMd5);

        $headers = array(
            'Authorization' => $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
================================================
<?php
namespace Upyun;

use Upyun\Api\Rest;
use Upyun\Api\Form;
use GuzzleHttp\Psr7;
use GuzzleHttp\Pool;
use GuzzleHttp\Client;

class Uploader
{
    /**
     * @var Config
     */
    protected $config;

    protected $useBlock = false;


    public function __construct(Config $config)
    {
        $this->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
================================================
<?php
/**
 * 又拍云 PHP-SDK
 */
namespace Upyun;

use Upyun\Api\Rest;

use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use GuzzleHttp;

/**
 * Class Upyun
 *
 * 又拍云云存储、云处理接口
 *
 * Upyun 类实现了又拍云云存储和云处理的所有接口,通过该类可以实现文件上传、下载;图片视频等多媒体资源云处理。
 * 本文档中,提到的"服务"是指又拍云文件加速回又拍云源类型的服务(即原先的存储类空间)。
 *
 * @package Upyun
 */
class Upyun
{

    /**
     * @var Config: 服务配置
     */
    protected $config;

    // 异步云处理任务类型
    /**
     * @var string 异步音视频处理
     */
    public static $PROCESS_TYPE_MEDIA = 'media';
    /**
     * @var string 文件压缩
     */
    public static $PROCESS_TYPE_ZIP = 'zip-file';
    /**
     * @var string 解压缩
     */
    public static $PROCESS_TYPE_UNZIP = 'unzip-file';
    /**
     * @var string 文件拉取
     */
    public static $PROCESS_TYPE_SYNC_FILE = 'sync-remote-file-to-upyun';
    /**
     * @var string 文档转换
     */
    public static $PROCESS_TYPE_CONVERT = 'document-type-convert';
    /**
     * @var string 异步图片拼接
     */
    public static $PROCESS_TYPE_STITCH = 'picture-stitch';

    /**
     * Upyun constructor.
     *
     * @param Config $config 服务配置
     */
    public function __construct(Config $config)
    {
        $this->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
================================================
<?php
namespace Upyun;

class Util
{
    public static function trim($str)
    {
        if (is_array($str)) {
            return array_map(array('Util', 'trim'), $str);
        } else {
            return trim($str);
        }
    }

    public static function getHeaderParams($headers, $otherParams = array())
    {
        $params = [];
        $otherParams = array_map('strtolower', $otherParams);
        foreach ($headers as $header => $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
================================================
<?php
namespace Upyun\Tests;

use Upyun\Signature;
use Upyun\Config;

class SignatureTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @var Config;
     */
    public $config;

    public function setUp()
    {
        $this->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
================================================
<?php
namespace Upyun\Tests;

use Upyun\Config;
use Upyun\Upyun;

class UpyunTest extends \PHPUnit_Framework_TestCase
{

    /**
     * @var Upyun
     */
    public static $upyun;


    protected static $taskId;

    protected static $tempFilePath;

    public static function setUpBeforeClass()
    {
        $config = new Config(BUCKET, USER_NAME, PWD);
        $config->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
================================================
<?php
require dirname(__DIR__) . '/vendor/autoload.php';

define('USER_NAME', 'tester');
define('PWD', 'grjxv2mxELR3');
define('BUCKET', 'sdkimg');
define('PIC_PATH', dirname(__FILE__) . '/assets/sample.jpeg');
define('PIC_SIZE', filesize(PIC_PATH));

function getFileUrl($path)
{
    return "http://" . BUCKET . ".b0.upaiyun.com/" . ltrim($path, '/');
}

function getUpyunFileSize($path)
{
    $url = getFileUrl($path);
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $return = curl_exec($ch);
    preg_match('~^HTTP/1.1 (\d{3})~', $return, $match1);
    preg_match('~Content-Length: (\d+)~', $return, $match2);

    if (isset($match1[1]) && $match1[1] == 200) {
        return isset($match2[1]) ? intval($match2[1]) : false;
    } else {
        return false;
    }
}
Download .txt
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
Download .txt
SYMBOL INDEX (121 symbols across 16 files)

FILE: RoboFile.php
  class RoboFile (line 7) | class RoboFile extends \Robo\Tasks
    method release (line 15) | public function release($version)

FILE: examples/Ai.php
  function asyncAudit (line 22) | function asyncAudit($tasks, $appName)
  function imageAsyncAudit (line 34) | function imageAsyncAudit()
  function videoAsyncAudit (line 48) | function videoAsyncAudit()
  function liveAudit (line 62) | function liveAudit()
  function liveAuditCancel (line 80) | function liveAuditCancel($taskID)

FILE: examples/Async-Process.php
  function videoAsyncProcess (line 20) | function videoAsyncProcess()
  function compress (line 38) | function compress()
  function depress (line 55) | function depress()
  function spiderman (line 72) | function spiderman()
  function fileAsyncConvert (line 89) | function fileAsyncConvert()
  function imageAsyncJoint (line 106) | function imageAsyncJoint()

FILE: examples/Pre-Process.php
  function formAsyncPreProcess (line 19) | function formAsyncPreProcess($file, $key, $apps=array())
  function formImageAsyncProcess (line 42) | function formImageAsyncProcess()
  function formImageSyncProcess (line 57) | function formImageSyncProcess()
  function formVideoAsyncProcess (line 80) | function formVideoAsyncProcess()
  function formDocAsyncConvert (line 96) | function formDocAsyncConvert()
  function formImageAsyncAudit (line 110) | function formImageAsyncAudit()
  function formVideoAsyncAudit (line 123) | function formVideoAsyncAudit()

FILE: src/Upyun/Api/Form.php
  class Form (line 9) | class Form extends Rest
    method upload (line 11) | public function upload($path, $stream, $params)

FILE: src/Upyun/Api/Pretreat.php
  class Pretreat (line 9) | class Pretreat
    method __construct (line 17) | public function __construct(Config $config)
    method process (line 25) | public function process($tasks, $optionalParams = array())
    method query (line 56) | public function query($taskIds, $path)

FILE: src/Upyun/Api/Rest.php
  class Rest (line 11) | class Rest
    method __construct (line 29) | public function __construct(Config $config)
    method request (line 35) | public function request($method, $storagePath)
    method withFile (line 48) | public function withFile($file)
    method send (line 59) | public function send()
    method withHeader (line 91) | public function withHeader($header, $value)
    method withHeaders (line 99) | public function withHeaders($headers)
    method toRequest (line 109) | public function toRequest()

FILE: src/Upyun/Api/SyncVideo.php
  class SyncVideo (line 13) | class SyncVideo {
    method __construct (line 19) | public function __construct(Config $config)
    method process (line 24) | public function process($params, $path) {

FILE: src/Upyun/Config.php
  class Config (line 9) | class Config
    method __construct (line 104) | public function __construct($serviceName, $operatorName, $operatorPass...
    method setOperatorPassword (line 114) | public function setOperatorPassword($operatorPassword)
    method getFormApiKey (line 119) | public function getFormApiKey()
    method setFormApiKey (line 128) | public function setFormApiKey($key)
    method getVersion (line 133) | public function getVersion()
    method getPretreatEndPoint (line 138) | public function getPretreatEndPoint()
    method getSyncVideoEndPoint (line 143) | public function getSyncVideoEndPoint()
    method getProtocol (line 148) | public function getProtocol()
    method setUploadType (line 153) | public function setUploadType($uploadType)
    method setConcurrency (line 158) | public function setConcurrency($concurrency)

FILE: src/Upyun/Signature.php
  class Signature (line 8) | class Signature
    method getHeaderSign (line 33) | public static function getHeaderSign($serviceConfig, $method, $path, $...
    method getPurgeSignHeader (line 56) | public static function getPurgeSignHeader(Config $serviceConfig, $urlS...
    method getBodySignature (line 78) | public static function getBodySignature(Config $serviceConfig, $method...

FILE: src/Upyun/Uploader.php
  class Uploader (line 10) | class Uploader
    method __construct (line 20) | public function __construct(Config $config)
    method upload (line 25) | public function upload($path, $file, $params, $withAsyncProcess)
    method pointUpload (line 58) | private function pointUpload($path, $stream, $params)
    method needUseBlock (line 113) | private function needUseBlock($fileSize)
    method concurrentPointUpload (line 135) | private function concurrentPointUpload($path, $stream, $params)

FILE: src/Upyun/Upyun.php
  class Upyun (line 23) | class Upyun
    method __construct (line 62) | public function __construct(Config $config)
    method setConfig (line 76) | public function setConfig(Config $config)
    method write (line 116) | public function write($path, $content, $params = array(), $withAsyncPr...
    method read (line 144) | public function read($path, $saveHandler = null, $params = array())
    method has (line 178) | public function has($path)
    method info (line 207) | public function info($path, $otherHeaders = array())
    method getMimetype (line 220) | public function getMimetype($path)
    method delete (line 238) | public function delete($path, $async = false)
    method createDir (line 257) | public function createDir($path)
    method deleteDir (line 275) | public function deleteDir($path)
    method usage (line 287) | public function usage($path = '/')
    method copy (line 305) | public function copy($source, $target)
    method move (line 323) | public function move($source, $target)
    method purge (line 340) | public function purge($urls)
    method process (line 397) | public function process($tasks, $type, $source = '')
    method queryProcessStatus (line 445) | public function queryProcessStatus($taskIds)
    method queryProcessResult (line 472) | public function queryProcessResult($taskIds)
    method m3u8Concat (line 485) | public function m3u8Concat($files, $saveAs)
    method m3u8Clip (line 504) | public function m3u8Clip($file, $saveAs, $slice = array(), $isInclude ...
    method m3u8Meta (line 526) | public function m3u8Meta($file)
    method snapshot (line 544) | public function snapshot($file, $saveAs, $point, $size = '', $format =...
    method avMeta (line 567) | public function avMeta($file)

FILE: src/Upyun/Util.php
  class Util (line 4) | class Util
    method trim (line 6) | public static function trim($str)
    method getHeaderParams (line 15) | public static function getHeaderParams($headers, $otherParams = array())
    method parseDir (line 30) | public static function parseDir($body)
    method base64Json (line 47) | public static function base64Json($params)
    method stringifyHeaders (line 52) | public static function stringifyHeaders($headers)
    method md5Hash (line 61) | public static function md5Hash($resource)
    method encodeURI (line 75) | public static function encodeURI($url)
    method isSuccess (line 91) | public static function isSuccess($code)

FILE: tests/SignatureTest.php
  class SignatureTest (line 7) | class SignatureTest extends \PHPUnit_Framework_TestCase
    method setUp (line 15) | public function setUp()
    method testGetBodySignature (line 20) | public function testGetBodySignature()

FILE: tests/UpyunTest.php
  class UpyunTest (line 7) | class UpyunTest extends \PHPUnit_Framework_TestCase
    method setUpBeforeClass (line 20) | public static function setUpBeforeClass()
    method tearDownAfterClass (line 30) | public static function tearDownAfterClass()
    method testWriteString (line 35) | public function testWriteString()
    method testWriteStream (line 44) | public function testWriteStream()
    method testWriteWithAsyncProcess (line 56) | public function testWriteWithAsyncProcess()
    method testWriteWithException (line 78) | public function testWriteWithException()
    method testReadFile (line 92) | public function testReadFile()
    method testDeleteFile (line 111) | public function testDeleteFile()
    method testDeleteNotExistsFile (line 127) | public function testDeleteNotExistsFile()
    method testHas (line 134) | public function testHas()
    method testInfo (line 149) | public function testInfo()
    method testGetMimetype (line 160) | public function testGetMimetype()
    method testCreateDir (line 168) | public function testCreateDir()
    method testReadDir (line 176) | public function testReadDir()
    method testDeleteDir (line 193) | public function testDeleteDir()
    method testUsage (line 202) | public function testUsage()
    method testCopy (line 211) | public function testCopy()
    method testMove (line 224) | public function testMove()
    method testPurge (line 235) | public function testPurge()
    method testProcess (line 246) | public function testProcess()
    method testQueryProcessStatus (line 267) | public function testQueryProcessStatus()
    method testQueryProcessResult (line 277) | public function testQueryProcessResult()
    method testAvMeta (line 285) | public function testAvMeta()
    method testSnapshot (line 294) | public function testSnapshot()
    method testParallelUpload (line 303) | public function testParallelUpload()

FILE: tests/bootstrap.php
  function getFileUrl (line 10) | function getFileUrl($path)
  function getUpyunFileSize (line 15) | function getUpyunFileSize($path)
Condensed preview — 34 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (108K chars).
[
  {
    "path": ".editorconfig",
    "chars": 199,
    "preview": "root = true\n\n[*.{php,html}]\nindent_style = space\nindent_size = 4\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespa"
  },
  {
    "path": ".gitattributes",
    "chars": 121,
    "preview": "* text=auto\n\n/examples    export-ignore\n/tests       export-ignore\n/doc.md      export-ignore\n/phpunit.xml export-ignore"
  },
  {
    "path": ".gitignore",
    "chars": 76,
    "preview": ".DS_Store\n.idea\nvendor\ncomposer.lock\nrelease*\n.php_cs.cache\ncoverage.clover\n"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 436,
    "preview": "build:\n    environment:\n        php:\n            version: 5.5\n    dependencies:\n        before:\n            - composer i"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 422,
    "preview": "## 3.3.0\n\n- 增加同步视频处理功能\n\n## 3.0.0\n\n- 重写 API 接口,不兼容 2.x 版本\n- 集合分块、刷新、视频预处理功能\n\n## 2.2.0\n\n- 增加 composer 支持,特别感谢 [@totoleo](h"
  },
  {
    "path": "LICENSE",
    "chars": 1049,
    "preview": "Copyright (c) 2016 UPYUN\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software "
  },
  {
    "path": "README.md",
    "chars": 3696,
    "preview": "# 又拍云 SDK for PHPer\n[![Build Status](https://scrutinizer-ci.com/g/upyun/php-sdk/badges/build.png?b=master)](https://scru"
  },
  {
    "path": "RoboFile.php",
    "chars": 1199,
    "preview": "<?php\n/**\n * This is project's console commands configuration for Robo task runner.\n *\n * @see http://robo.li/\n */\nclass"
  },
  {
    "path": "composer.json",
    "chars": 1031,
    "preview": "{\n    \"name\": \"upyun/sdk\",\n    \"description\": \"UPYUN sdk for php\",\n    \"keywords\": [\"UPYUN\", \"sdk\"],\n    \"type\": \"librar"
  },
  {
    "path": "doc.md",
    "chars": 8121,
    "preview": "# SDK 文档\n\n又拍云 php sdk 中所有封装的接口,均通过 `Upyun\\Upyun` 类封装,后续新版本也会保持\n该类方法向下兼容,sdk 中其他类及其方法不保证兼容性。如果有使用疑问欢迎提 [issues](https://g"
  },
  {
    "path": "examples/Ai.php",
    "chars": 2130,
    "preview": "<?php\n/**\n * 又拍云 PHP-SDK 人工智能使用实例\n * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数\n */\n\nrequire  __DIR__ . '/User-Pro"
  },
  {
    "path": "examples/Async-Process.php",
    "chars": 3038,
    "preview": "<?php\n/**\n * 又拍云 PHP-SDK 异步云处理使用实例\n * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数\n */\n\nrequire  __DIR__ . '/User-Pr"
  },
  {
    "path": "examples/Pre-Process.php",
    "chars": 2713,
    "preview": "<?php\n/**\n * 又拍云 PHP-SDK 上传预处理使用实例\n * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置(User-Profle.php),参数\n */\n\nrequire  __DIR__ . '/User-Pr"
  },
  {
    "path": "examples/README.md",
    "chars": 254,
    "preview": "# php sdk examples\n\n这些 examples 旨在帮助您快速了解使用又拍云 php-sdk。\n\n这些 examples 都可以直接运行,但是在运行之前,需要根据您的需求填写相应的参数。\n\n### 参数:\n\n用户个人参数见 "
  },
  {
    "path": "examples/User-Profile.php",
    "chars": 1498,
    "preview": "<?php\n/**\n * 又拍云 PHP-SDK examle 用户配置文件\n * 测试与运行例子的时,用户需要根据自己的需求填写对应的配置,参数\n */\n\nrequire dirname(__DIR__) . '/vendor/autol"
  },
  {
    "path": "examples/client-upload/Readme.md",
    "chars": 410,
    "preview": "## 客户端上传\n\n本示例展示了如何使用表单 API, 直接从客户端进行安全的文件上传, 这种方式不需要客户服务器进行中转, 节省了客户服务器流量, 并且支持 HTTP/HTTPS 两种协议\n\nDEMO 使用 `sdkimg` 空间进行演示"
  },
  {
    "path": "examples/client-upload/index.html",
    "chars": 1987,
    "preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>Document</title>\n    <link rel=\"stylesheet"
  },
  {
    "path": "examples/client-upload/normalize.css",
    "chars": 7925,
    "preview": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Pre"
  },
  {
    "path": "examples/client-upload/policy.php",
    "chars": 568,
    "preview": "<?php\nrequire  __DIR__ . '/../../tests/bootstrap.php';\n\nuse Upyun\\Config;\nuse Upyun\\Signature;\nuse Upyun\\Util;\n\n$config "
  },
  {
    "path": "examples/list-all-file.php",
    "chars": 816,
    "preview": "<?php\nrequire  __DIR__ . '/../tests/bootstrap.php';\n\nuse Upyun\\Config;\nuse Upyun\\Upyun;\n\n$config = new Config(BUCKET, US"
  },
  {
    "path": "phpunit.xml",
    "chars": 783,
    "preview": "<phpunit backupGlobals=\"true\"\n         backupStaticAttributes=\"false\"\n         colors=\"true\"\n         convertErrorsToExc"
  },
  {
    "path": "src/Upyun/Api/Form.php",
    "chars": 1242,
    "preview": "<?php\n\nnamespace Upyun\\Api;\n\nuse Upyun\\Signature;\nuse Upyun\\Util;\nuse GuzzleHttp\\Client;\n\nclass Form extends Rest\n{\n    "
  },
  {
    "path": "src/Upyun/Api/Pretreat.php",
    "chars": 2216,
    "preview": "<?php\nnamespace Upyun\\Api;\n\nuse GuzzleHttp\\Client;\nuse Upyun\\Config;\nuse Upyun\\Signature;\nuse Upyun\\Util;\n\nclass Pretrea"
  },
  {
    "path": "src/Upyun/Api/Rest.php",
    "chars": 2989,
    "preview": "<?php\n\nnamespace Upyun\\Api;\n\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Psr7;\nuse Upyun\\Config;\nuse Upyun\\Signature;\nuse Upyu"
  },
  {
    "path": "src/Upyun/Api/SyncVideo.php",
    "chars": 889,
    "preview": "<?php\n/**\n * 同步视频处理\n */\n\nnamespace Upyun\\Api;\n\nuse GuzzleHttp\\Client;\nuse Upyun\\Config;\nuse Upyun\\Signature;\n\n\nclass Syn"
  },
  {
    "path": "src/Upyun/Config.php",
    "chars": 3279,
    "preview": "<?php\nnamespace Upyun;\n\n/**\n * Class Config\n *\n * @package Upyun\n */\nclass Config\n{\n    /**\n     * @var string 服务名称,将会被弃"
  },
  {
    "path": "src/Upyun/Signature.php",
    "chars": 2515,
    "preview": "<?php\nnamespace Upyun;\n\n/**\n * Class Signature\n * @package Upyun\n */\nclass Signature\n{\n    /**\n     * 获取分块上传接口的签名\n     *"
  },
  {
    "path": "src/Upyun/Uploader.php",
    "chars": 6600,
    "preview": "<?php\nnamespace Upyun;\n\nuse Upyun\\Api\\Rest;\nuse Upyun\\Api\\Form;\nuse GuzzleHttp\\Psr7;\nuse GuzzleHttp\\Pool;\nuse GuzzleHttp"
  },
  {
    "path": "src/Upyun/Upyun.php",
    "chars": 17090,
    "preview": "<?php\n/**\n * 又拍云 PHP-SDK\n */\nnamespace Upyun;\n\nuse Upyun\\Api\\Rest;\n\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\Psr7;\nuse Guzz"
  },
  {
    "path": "src/Upyun/Util.php",
    "chars": 2615,
    "preview": "<?php\nnamespace Upyun;\n\nclass Util\n{\n    public static function trim($str)\n    {\n        if (is_array($str)) {\n         "
  },
  {
    "path": "tests/SignatureTest.php",
    "chars": 516,
    "preview": "<?php\nnamespace Upyun\\Tests;\n\nuse Upyun\\Signature;\nuse Upyun\\Config;\n\nclass SignatureTest extends \\PHPUnit_Framework_Tes"
  },
  {
    "path": "tests/UpyunTest.php",
    "chars": 9472,
    "preview": "<?php\nnamespace Upyun\\Tests;\n\nuse Upyun\\Config;\nuse Upyun\\Upyun;\n\nclass UpyunTest extends \\PHPUnit_Framework_TestCase\n{\n"
  },
  {
    "path": "tests/bootstrap.php",
    "chars": 893,
    "preview": "<?php\nrequire dirname(__DIR__) . '/vendor/autoload.php';\n\ndefine('USER_NAME', 'tester');\ndefine('PWD', 'grjxv2mxELR3');\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the upyun/php-sdk GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 34 files (86.7 KB), approximately 27.8k tokens, and a symbol index with 121 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!