├── .coveralls.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── _ide_helper.php ├── composer.json ├── phpunit.xml.dist ├── samples ├── AliyunOssFlysystem.php ├── Common.php └── Config.php ├── src ├── AliyunOssAdapter.php └── Plugins │ └── PutFile.php └── tests └── AliyunOssAdapterTest.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | coverage_clover: coverage.xml 3 | json_path: coverage.json 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | doc 4 | output 5 | .idea 6 | .buildpath 7 | .project 8 | .settings 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 7.0 4 | - 5.6 5 | - 5.5 6 | install: 7 | - composer self-update 8 | - composer install --no-interaction 9 | script: 10 | - php vendor/bin/phpunit 11 | after_success: 12 | - php vendor/bin/coveralls -v 13 | env: 14 | global: 15 | - secure: Fjxplc6yvUila1PItv2oKG/Dw2C4dx/KHKuA+SwaHi00csfB7c4ByAQGq7dXHTgjSV86VRARmA3pRtb3vz8XlNpFoCNFcb5vx/u/9VfjwuvOOVattSRYAAml9Nl90QWbdk1EJYAfFcjtbOhRzXf2sFc9kC1eVnizhGxghBQEp0HCW2lUg7v92WDuMWXJIDh/qlF90m0TnT43STjSiYdmz0cTfgtF5+LdxEeDD5xFB5uVS+3+oW0fwKPdPaWaNrGvc0AovBvktd1epzZr+/+3nmsJMXMEAe3WJYJM9cSvkyNYZ2DPNQWH51rFe7yDYvx9fooSMBBxqmFbgbKIz3eHZc+zATTAoJjnJBP5Yks5Vm+iyk9MUf+qZKFXbKCkKOir1U6z0UiobFjBySUkmF3DezViXyxlTv1TA/9RhfvPr1UOLhp1C9hLuJcXvHS0b4UH2429SHBo+/fLrywOy0pclhjRjYeCdUhypjNUOuCQCw5Kk8AeWAMnMb8ZPdzGBEZ3D1qIhVfb/wBph2IarUHsHyCsCZgaIH9NYo2/VLu5nvMmyc60rXE6/MnVor4C4rywl/8nR+ENJwC1/0FMa1hiBlH/eweWmK09Z/+ZPXNA1GxEfjez1lAA1xSVmy+i2J7XYk1h9pZz4Iz4tfy0Ny/6d1FPKnZPEry7MmrYXxF4zh0= 16 | - secure: If7v3xW3147lYHWxLkvvun8+BbzFJaI5RPDE3tatI0o35snxpVvwue6dMlJKycoobfZNJMX2arrepq+YNaZEpPTUCdzqYO0J8WIh5CMTcKP11Ut78BNQF0cwp18OeT9wDitSKb39G/dhlD8SpFTUGMJqEjR2b50XVEf/2m0bs+g3clCr0j0bubGlmY0RIgwf5dKR/bTDTGFcx3WxHcVDReoEiLXHcHZj5JOWUkTiJ3MLV8dZxx8SlCvPGnno4aL/M54KxKQR70R37AlXGaSxQBtWqtvxEdJgqMfF5O/VMT0gq+Ea4T6TtkN4sWBTPg9YAdt+xdfSpG8dAbpdCy/wfr/n+Ijy4OBJ6vQk+P3creh4sv+FbvPy1vVEJHQfY2IqV+3WUI+oXauoWXGQG9WmHFc8NRMrp+AZNKwHUTpL6hXZa2TVVERI3nI94leIKNSdPV2jwHerFlteSVSpN2AERirZl1WGbg6ccwyqpE+KamO6kKMveevM8fPWusWIV1XLifLvoecWSeyvwSOv6q7s1/swV2xIw1FJi0dhL2l9STw5el0KIhruoYJuwXYB71sr65oQnn9jiGReAFWoyrJT4e0+s8L+ncKgSdiYT8fW0b99ihVZYsiQ8hOWnWDcwUn+7iOYQPHermRPHDOHjcu7DYPECRPu17C5rZBRCQiGmA8= 17 | - secure: NHd1e4QoMHH1hDzjk/6/K9Q1P6Z7US7x1Y6cCvY+J7zGgjrkKO6g9On1BbT9/mz9XJjrvZ6LnllDFtx7XxPn0du9A9+Axemk2NGUN4db4eBtvSFXzJiru+8x8fd5XAT0VG81/VNNqleilm45nLV/KFYyU+ztnujTCy3b6p7NqgGW78VpgDwB4QZEx0wzznSDkAfgTdupGKj+j1b56rGc+lKY0v+TW4N5l8zWdAYvDI79EjrEsLsAN3n1XM13k5HsORUDZ40TCYFtyQX6B22w4p/GS6TXc6yd+dXmkCodDjDGWoIG+J28ZMCPjpP3Pc8ovwq15A3rof6oRkTlnw0r0B6ZrfJA5eCB0BWXZ89XQ4H7p5zJrqUksaVdmNPOPz3ivR3s4t341QiMoHU9HWKAcifJRPEhaggoAZPiSTLnvejWhWHxRl1BHsRrZoSpv3tpqZBCeWVkt56uMRwsJK92YM2KYzrRFxbXpPqYzeycNxBXap02BqH5/QpVWpyxbx7CgGyssyiGGaTZ0TFQlM8x9UeuyFGB2Xkhgj4YseQehBY3LCknKHdckhjDEGdeCsiuCqWn/BuJGuqOj0BYbhO6vs+hzhuwTVvBKUWcBUnIoiMsQtWdCDJD2zrI6vG1e0yuwwpPYXd/V4aN6pwryrk7JrgHRRE9yPtiufwob1UeRmo= 18 | - secure: fnPDcXx5rhthiEcwqUEUQRyYbBTc9cn5czstyKVJMdZL+lH9J/P4qQfXg3ZwlotqR2lmrxnYQKHMco7eTrOUc1pHIORfou+DpNJ7Y+V5+QZYORDYRtvuKhqyNKq85LRQRhWA6ya2swhE5TUu10I8qF2vMqaFrM3VpAt754sM/pRnb48H4ZeO5mELizNRjHWAjyjSxs4b3I9gD6dBfcI8zOBTZXgbb4e8Qk/3NXXut601SpP6Vl7W+ngM05LwK8IqMYnq7TFIHVnTIJOu8SnVgO7KA4aBrR6ND95dt4pEORyKQfdgcrPn4Ma3S8I8uW8Ef/hGxx3ZkuB806mezdgsuQjAWYSm64IY1xty0nE5nvTuUmW/swBiUn5WjJGSZ8brzwSfb/22dFYkqMTMPn5g1M5sTgnxKvsLiSWVvY1xkUk9yqUdQiJ08i07Ce59niwzUCvEfJA4Wk2oPF7/L5KvPnQq899UCjrs4eRElvUJ/Ygi/0Wy4o1QrcSZENqd5ZZuDnowzw/xrmvSIkWhDPwaQQuBXKiKUOLgv8SE3uNMUL0CNKGTZwWw1FKioPQ4wlErKreVGxuMAmuaE1qcBgtpDgdayw/CfCfwJl8Z3CIJwoXfrlom6EGJZOftzCuzcyrc3iCSbaLcBpWVTecPUqlCkDlKknDvaOSnIB8ULyk44qM= 19 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## 1.1.1 / 2017-08-03 4 | 5 | * release package 6 | 7 | ## 1.0.0 / 2017-07-21 8 | 9 | * AliyunOssAdapter 实现 League\Flysystem\Adapter\AbstractAdapter 定义的所有接口 10 | * 添加 AliyunOssServiceProvider 用于 Laravel 注册 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | #The MIT License (MIT) 2 | 3 | Copyright (c) Aliyun and other contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flysystem Adapter for AliCloud OSS 2 | 3 | [![Build Status](https://travis-ci.org/aliyun/aliyun-oss-php-sdk-flysystem.svg?branch=master)](https://travis-ci.org/aliyun/aliyun-oss-php-sdk-flysystem) 4 | [![Coverage Status](https://coveralls.io/repos/github/aliyun/aliyun-oss-php-sdk-flysystem/badge.svg?branch=master)](https://coveralls.io/github/aliyun/aliyun-oss-php-sdk-flysystem?branch=master) 5 | 6 | This is a Flysystem Adapter for the AliCloud OSS ~1.2.1 7 | 8 | ## Installation 9 | 10 | ```bash 11 | composer require aliyuncs/aliyun-oss-flysystem 12 | ``` 13 | 14 | ## Running Sample 15 | 16 | ``` 17 | cd vendor/aliyun/aliyun-oss-flysystem/ 18 | 19 | vim samples/Config.php 20 | 21 | modify the following config: 22 | const OSS_ACCESS_ID = ''; 23 | const OSS_ACCESS_KEY = ''; 24 | const OSS_ENDPOINT = ''; 25 | const OSS_TEST_BUCKET = ''; 26 | 27 | php samples/AliyunOssFlysystem.php 28 | ``` 29 | 30 | ## Running Test 31 | 32 | ```bash 33 | export OSS_ACCESS_KEY_ID=your id 34 | export OSS_ACCESS_KEY_SECRET=your secret 35 | export OSS_ENDPOINT=your endpoint 36 | export OSS_BUCKET=your bucket 37 | 38 | cd vendor/aliyun/aliyun-oss-flysystem/ 39 | 40 | composer install 41 | 42 | php vendor/bin/phpunit 43 | ``` 44 | 45 | ## License 46 | - [MIT](https://github.com/aliyun/aliyun-oss-php-sdk-flysystem/blob/master/LICENSE.md) 47 | -------------------------------------------------------------------------------- /_ide_helper.php: -------------------------------------------------------------------------------- 1 | =5.5.9", 13 | "league/flysystem": "~1.0", 14 | "aliyuncs/oss-sdk-php": "~2.2.1" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "~4.0", 18 | "satooshi/php-coveralls": "~1.0" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Aliyun\\Flysystem\\AliyunOss\\": "src/" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ./tests/ 19 | 20 | 21 | 22 | 23 | 24 | ./src/AliyunOssAdapter.php 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /samples/AliyunOssFlysystem.php: -------------------------------------------------------------------------------- 1 | addPlugin(new PutFile()); 22 | 23 | 24 | $adapter->deleteDir('aliyuncs-flysystem-samples'); 25 | $adapter->setPathPrefix('aliyuncs-flysystem-samples'); 26 | 27 | // 写字符串到oss文件 28 | $filesystem->write('1.txt', '123'); 29 | 30 | // 判断oss文件是否存在 31 | $isTrue = $filesystem->has('1.txt'); 32 | Common::println("1.txt is exsit ?" . $isTrue); 33 | 34 | // 读oss文件到本地变量 35 | $data = $filesystem->read('1.txt'); 36 | Common::println("read content is". $data); 37 | 38 | // 更新oss文件 39 | $filesystem->update('1.txt', '456'); 40 | $data = $filesystem->read('1.txt'); 41 | Common::println("update content is" . $data); 42 | 43 | // 重命名oss文件 44 | $filesystem->rename('1.txt', '2.txt'); 45 | $isTrue = $filesystem->has('1.txt'); 46 | Common::println("1.txt is exsit ? ". $isTrue); 47 | 48 | // 判断oss文件是否存在 49 | $isTrue = $filesystem->has('2.txt'); 50 | Common::println("2.txt is exsit ? ". $isTrue); 51 | 52 | // 获取oss文件meta 53 | $result = $filesystem->getMetadata('2.txt'); 54 | echo "getMetadata result: "; 55 | print_r($result); 56 | 57 | // 获取oss文件mine type 58 | $result = $filesystem->getMimetype('2.txt'); 59 | Common::println("getMimetype result:". $result); 60 | 61 | // 获取oss文件时间戳 62 | $result = $filesystem->getTimestamp('2.txt'); 63 | Common::println("getTimestamp result:". $result); 64 | 65 | // 获取oss文件size 66 | $result = $filesystem->getSize('2.txt'); 67 | Common::println("getSize result:". $result); 68 | 69 | // 在oss上创建文件目录 70 | $filesystem->createDir('test'); 71 | // 拷贝oss文件 72 | $filesystem->copy('2.txt', 'test/3.txt'); 73 | 74 | // 按前缀list, true是递归list出所有文件,false只list当前文件夹的文件 75 | $result = $filesystem->listContents('', true); 76 | echo "listContents result: "; 77 | print_r($result); 78 | ?> 79 | -------------------------------------------------------------------------------- /samples/Common.php: -------------------------------------------------------------------------------- 1 | getMessage() . "\n"); 35 | return null; 36 | } 37 | return $ossClient; 38 | } 39 | 40 | public static function getBucketName() 41 | { 42 | return self::bucket; 43 | } 44 | 45 | /** 46 | * 工具方法,创建一个存储空间,如果发生异常直接exit 47 | */ 48 | public static function createBucket() 49 | { 50 | $ossClient = self::getOssClient(); 51 | if (is_null($ossClient)) exit(1); 52 | $bucket = self::getBucketName(); 53 | $acl = OssClient::OSS_ACL_TYPE_PUBLIC_READ; 54 | try { 55 | $ossClient->createBucket($bucket, $acl); 56 | } catch (OssException $e) { 57 | 58 | $message = $e->getMessage(); 59 | if (\OSS\Core\OssUtil::startsWith($message, 'http status: 403')) { 60 | echo "Please Check your AccessKeyId and AccessKeySecret" . "\n"; 61 | exit(0); 62 | } elseif (strpos($message, "BucketAlreadyExists") !== false) { 63 | echo "Bucket already exists. Please check whether the bucket belongs to you, or it was visited with correct endpoint. " . "\n"; 64 | exit(0); 65 | } 66 | printf(__FUNCTION__ . ": FAILED\n"); 67 | printf($e->getMessage() . "\n"); 68 | return; 69 | } 70 | print(__FUNCTION__ . ": OK" . "\n"); 71 | } 72 | 73 | public static function println($message) 74 | { 75 | if (!empty($message)) { 76 | echo strval($message) . "\n"; 77 | } 78 | } 79 | } 80 | 81 | Common::createBucket(); 82 | -------------------------------------------------------------------------------- /samples/Config.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | class AliyunOssAdapter extends AbstractAdapter 20 | { 21 | use StreamedTrait; 22 | use NotSupportingVisibilityTrait; 23 | 24 | /** 25 | * Aliyun Oss Client. 26 | * 27 | * @var \OSS\OssClient 28 | */ 29 | protected $client; 30 | 31 | /** 32 | * bucket name. 33 | * 34 | * @var string 35 | */ 36 | protected $bucket; 37 | 38 | /** 39 | * @var array 40 | */ 41 | protected $options = []; 42 | 43 | /** 44 | * @var array 45 | */ 46 | protected static $mappingOptions = [ 47 | 'mimetype' => OssClient::OSS_CONTENT_TYPE, 48 | 'size' => OssClient::OSS_LENGTH, 49 | ]; 50 | 51 | /** 52 | * Constructor. 53 | * 54 | * @param OssClient $client 55 | * @param string $bucket 56 | * @param string $prefix 57 | * @param array $options 58 | */ 59 | public function __construct(OssClient $client, $bucket, $prefix = null, array $options = []) 60 | { 61 | $this->client = $client; 62 | $this->bucket = $bucket; 63 | $this->setPathPrefix($prefix); 64 | $this->options = array_merge($this->options, $options); 65 | } 66 | 67 | /** 68 | * Get the Aliyun Oss Client bucket. 69 | * 70 | * @return string 71 | */ 72 | public function getBucket() 73 | { 74 | return $this->bucket; 75 | } 76 | 77 | /** 78 | * Get the Aliyun Oss Client instance. 79 | * 80 | * @return \OSS\OssClient 81 | */ 82 | public function getClient() 83 | { 84 | return $this->client; 85 | } 86 | 87 | /** 88 | * Write using a local file path. 89 | * 90 | * @param string $path 91 | * @param string $localFilePath 92 | * @param Config $config Config object 93 | * @return array|false false on failure file meta data on success 94 | */ 95 | public function putFile($path, $localFilePath, Config $config) 96 | { 97 | $object = $this->applyPathPrefix($path); 98 | $options = $this->getOptionsFromConfig($config); 99 | 100 | if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { 101 | $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, ''); 102 | } 103 | 104 | $this->client->uploadFile($this->bucket, $object, $localFilePath, $options); 105 | 106 | $type = 'file'; 107 | $result = compact('type', 'path'); 108 | $result['mimetype'] = $options[OssClient::OSS_CONTENT_TYPE]; 109 | return $result; 110 | } 111 | 112 | /** 113 | * Write a new file. 114 | * 115 | * @param string $path 116 | * @param string $contents 117 | * @param Config $config Config object 118 | * @return array|false false on failure file meta data on success 119 | */ 120 | public function write($path, $contents, Config $config) 121 | { 122 | $object = $this->applyPathPrefix($path); 123 | $options = $this->getOptionsFromConfig($config); 124 | 125 | if (! isset($options[OssClient::OSS_LENGTH])) { 126 | $options[OssClient::OSS_LENGTH] = Util::contentSize($contents); 127 | } 128 | 129 | if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { 130 | $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, $contents); 131 | } 132 | 133 | $this->client->putObject($this->bucket, $object, $contents, $options); 134 | 135 | $type = 'file'; 136 | $result = compact('type', 'path', 'contents'); 137 | $result['mimetype'] = $options[OssClient::OSS_CONTENT_TYPE]; 138 | $result['size'] = $options[OssClient::OSS_LENGTH]; 139 | return $result; 140 | } 141 | 142 | /** 143 | * Update a file. 144 | * 145 | * @param string $path 146 | * @param string $contents 147 | * @param Config $config Config object 148 | * @return array|false false on failure file meta data on success 149 | */ 150 | public function update($path, $contents, Config $config) 151 | { 152 | if (! $config->has('visibility') && ! $config->has('ACL')) { 153 | $config->set('ACL', $this->getObjectACL($path)); 154 | } 155 | return $this->write($path, $contents, $config); 156 | } 157 | 158 | /** 159 | * Rename a file. 160 | * 161 | * @param string $path 162 | * @param string $newpath 163 | * @return bool 164 | */ 165 | public function rename($path, $newpath) 166 | { 167 | $this->copy($path, $newpath); 168 | $this->delete($path); 169 | } 170 | 171 | /** 172 | * Copy a file. 173 | * 174 | * @param string $path 175 | * @param string $newpath 176 | * @return bool 177 | */ 178 | public function copy($path, $newpath) 179 | { 180 | $object = $this->applyPathPrefix($path); 181 | $newobject = $this->applyPathPrefix($newpath); 182 | 183 | $this->client->copyObject($this->bucket, $object, $this->bucket, $newobject); 184 | } 185 | 186 | /** 187 | * Delete a file. 188 | * 189 | * @param string $path 190 | * @return bool 191 | */ 192 | public function delete($path) 193 | { 194 | $object = $this->applyPathPrefix($path); 195 | 196 | $this->client->deleteObject($this->bucket, $object); 197 | } 198 | 199 | /** 200 | * Delete a directory. 201 | * 202 | * @param string $dirname 203 | * @return bool 204 | */ 205 | public function deleteDir($dirname) 206 | { 207 | $list = $this->listContents($dirname, true); 208 | 209 | $objects = []; 210 | foreach ($list as $val) { 211 | if ($val['type'] === 'file') { 212 | $objects[] = $this->applyPathPrefix($val['path']); 213 | } else { 214 | $objects[] = $this->applyPathPrefix($val['path']) .'/'; 215 | } 216 | } 217 | 218 | $this->client->deleteObjects($this->bucket, $objects); 219 | } 220 | 221 | /** 222 | * Create a directory. 223 | * 224 | * @param string $dirname directory name 225 | * @param Config $config 226 | * @return array|false 227 | */ 228 | public function createDir($dirname, Config $config) 229 | { 230 | $object = $this->applyPathPrefix($dirname); 231 | $options = $this->getOptionsFromConfig($config); 232 | 233 | $this->client->createObjectDir($this->bucket, $object, $options); 234 | 235 | return ['path' => $dirname, 'type' => 'dir']; 236 | } 237 | 238 | /** 239 | * Check whether a file exists. 240 | * 241 | * @param string $path 242 | * @return bool 243 | */ 244 | public function has($path) 245 | { 246 | $object = $this->applyPathPrefix($path); 247 | 248 | if ($this->client->doesObjectExist($this->bucket, $object)) 249 | { 250 | return true; 251 | } 252 | 253 | return $this->doesDirectoryExist($object); 254 | } 255 | 256 | /** 257 | * Read a file. 258 | * 259 | * @param string $path 260 | * @return array|false 261 | */ 262 | public function read($path) 263 | { 264 | $object = $this->applyPathPrefix($path); 265 | $contents = $this->client->getObject($this->bucket, $object); 266 | return compact('contents', 'path'); 267 | } 268 | 269 | /** 270 | * List contents of a directory. 271 | * 272 | * @param string $directory 273 | * @param bool $recursive 274 | * @return array 275 | */ 276 | public function listContents($directory = '', $recursive = false) 277 | { 278 | $directory = $this->applyPathPrefix($directory); 279 | $directory = $this->applyPathSeparator($directory); 280 | 281 | $bucket = $this->bucket; 282 | $delimiter = '/'; 283 | $nextMarker = ''; 284 | $maxkeys = 1000; 285 | $options = [ 286 | 'delimiter' => $delimiter, 287 | 'prefix' => $directory, 288 | 'max-keys' => $maxkeys, 289 | 'marker' => $nextMarker, 290 | ]; 291 | 292 | $listObjectInfo = $this->client->listObjects($bucket, $options); 293 | 294 | $objectList = $listObjectInfo->getObjectList(); // 文件列表 295 | $prefixList = $listObjectInfo->getPrefixList(); // 目录列表 296 | 297 | $result = []; 298 | foreach ($objectList as $objectInfo) { 299 | if ($objectInfo->getSize() === 0 && $directory === $objectInfo->getKey()) { 300 | $result[] = [ 301 | 'type' => 'dir', 302 | 'path' => $this->removePathPrefix(rtrim($objectInfo->getKey(), '/')), 303 | 'timestamp' => strtotime($objectInfo->getLastModified()), 304 | ]; 305 | continue; 306 | } 307 | 308 | $result[] = [ 309 | 'type' => 'file', 310 | 'path' => $this->removePathPrefix($objectInfo->getKey()), 311 | 'timestamp' => strtotime($objectInfo->getLastModified()), 312 | 'size' => $objectInfo->getSize(), 313 | ]; 314 | } 315 | 316 | foreach ($prefixList as $prefixInfo) { 317 | if ($recursive) { 318 | $next = $this->listContents($this->removePathPrefix($prefixInfo->getPrefix()), $recursive); 319 | $result = array_merge($result, $next); 320 | } else { 321 | $result[] = [ 322 | 'type' => 'dir', 323 | 'path' => $this->removePathPrefix(rtrim($prefixInfo->getPrefix(), '/')), 324 | 'timestamp' => 0, 325 | ]; 326 | } 327 | } 328 | 329 | return $result; 330 | } 331 | 332 | /** 333 | * Get all the meta data of a file or directory. 334 | * 335 | * @param string $path 336 | * @return array|false 337 | * @throws \OSS\Core\OssException 338 | */ 339 | public function getMetadata($path) 340 | { 341 | $object = $this->applyPathPrefix($path); 342 | 343 | $result = $this->client->getObjectMeta($this->bucket, $object); 344 | 345 | return [ 346 | 'type' => 'file', 347 | 'dirname' => Util::dirname($path), 348 | 'path' => $path, 349 | 'timestamp' => strtotime($result['last-modified']), 350 | 'mimetype' => $result['content-type'], 351 | 'size' => $result['content-length'], 352 | ]; 353 | } 354 | 355 | /** 356 | * Get all the meta data of a file or directory. 357 | * 358 | * @param string $path 359 | * @return array|false 360 | */ 361 | public function getSize($path) 362 | { 363 | return $this->getMetadata($path); 364 | } 365 | 366 | /** 367 | * Get the mimetype of a file. 368 | * 369 | * @param string $path 370 | * @return array|false 371 | */ 372 | public function getMimetype($path) 373 | { 374 | return $this->getMetadata($path); 375 | } 376 | 377 | /** 378 | * Get the timestamp of a file. 379 | * 380 | * @param string $path 381 | * @return array|false 382 | */ 383 | public function getTimestamp($path) 384 | { 385 | return $this->getMetadata($path); 386 | } 387 | 388 | /** 389 | * Get options from the config. 390 | * 391 | * @param Config $config 392 | * @return array 393 | */ 394 | protected function getOptionsFromConfig(Config $config) 395 | { 396 | $options = $this->options; 397 | foreach (static::$mappingOptions as $option => $ossOption) { 398 | if (! $config->has($option)) { 399 | continue; 400 | } 401 | $options[$ossOption] = $config->get($option); 402 | } 403 | 404 | return $options; 405 | } 406 | 407 | /** 408 | * {@inheritdoc} 409 | */ 410 | public function setVisibility($path, $visibility) 411 | { 412 | $object = $this->applyPathPrefix($path); 413 | $acl = ($visibility === AdapterInterface::VISIBILITY_PUBLIC ) ? 'public-read' : 'private'; 414 | $this->client->putObjectAcl($this->bucket, $object, $acl); 415 | return compact('visibility'); 416 | } 417 | 418 | /** 419 | * {@inheritdoc} 420 | */ 421 | public function getVisibility($path) 422 | { 423 | $bucket = $this->bucket; 424 | $object = $this->applyPathPrefix($path); 425 | $res['visibility'] = $this->client->getObjectAcl($bucket, $object); 426 | return $res; 427 | } 428 | 429 | /** 430 | * The the ACL visibility. 431 | * 432 | * @param string $path 433 | * 434 | * @return string 435 | */ 436 | protected function getObjectACL($path) 437 | { 438 | $metadata = $this->getVisibility($path); 439 | return $metadata['visibility'] === AdapterInterface::VISIBILITY_PUBLIC ? 'public-read' : 'private'; 440 | } 441 | 442 | protected function doesDirectoryExist($object) 443 | { 444 | // Maybe this isn't an actual key, but a prefix. 445 | // Do a prefix listing of objects to determine. 446 | 447 | $bucket = $this->bucket; 448 | $delimiter = '/'; 449 | $nextMarker = ''; 450 | $maxkeys = 1000; 451 | $prefix = rtrim($object, '/') . '/'; 452 | $options = [ 453 | 'delimiter' => $delimiter, 454 | 'prefix' => $prefix, 455 | 'max-keys' => $maxkeys, 456 | 'marker' => $nextMarker, 457 | ]; 458 | 459 | $listObjectInfo = $this->client->listObjects($bucket, $options); 460 | $objectList = $listObjectInfo->getObjectList(); // 文件列表 461 | $prefixList = $listObjectInfo->getPrefixList(); // 目录列表 462 | 463 | return $objectList || $prefixList; 464 | } 465 | 466 | protected function applyPathSeparator($path) 467 | { 468 | return rtrim($path, '\\/') . '/'; 469 | } 470 | 471 | } 472 | -------------------------------------------------------------------------------- /src/Plugins/PutFile.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | class PutFile extends AbstractPlugin 15 | { 16 | /** 17 | * Get the method name. 18 | * 19 | * @return string 20 | */ 21 | public function getMethod() 22 | { 23 | return 'putFile'; 24 | } 25 | 26 | /** 27 | * Handle. 28 | * 29 | * @param string $path 30 | * @param string $localFilePath 31 | * @param array $config 32 | * @return bool 33 | */ 34 | public function handle($path, $localFilePath, array $config = []) 35 | { 36 | if (! method_exists($this->filesystem, 'getAdapter')) { 37 | return false; 38 | } 39 | 40 | if (! method_exists($this->filesystem->getAdapter(), 'putFile')) { 41 | return false; 42 | } 43 | 44 | $config = new Config($config); 45 | if (method_exists($this->filesystem, 'getConfig')) { 46 | $config->setFallback($this->filesystem->getConfig()); 47 | } 48 | 49 | return (bool) $this->filesystem->getAdapter()->putFile($path, $localFilePath, $config); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/AliyunOssAdapterTest.php: -------------------------------------------------------------------------------- 1 | setPathPrefix($dir); 36 | 37 | $filesystem = new Filesystem($adapter); 38 | $filesystem->addPlugin(new PutFile()); 39 | 40 | $this->filesystem = $filesystem; 41 | } 42 | 43 | public function setUp() 44 | { 45 | $this->create_dir = time() . "test-create-dir"; 46 | 47 | $this->prepare_file = time() . "prepare-file"; 48 | $this->filesystem->write($this->prepare_file, 'xxx'); 49 | 50 | $this->rename_file = time() . "rename-file"; 51 | $this->filesystem->write($this->rename_file, 'xxx'); 52 | 53 | $this->delete_file = time() . "delete-file"; 54 | $this->filesystem->write($this->delete_file, 'xxx'); 55 | } 56 | 57 | public function tearDown() 58 | { 59 | if ($this->filesystem->has($this->prepare_file)) 60 | { 61 | $this->filesystem->delete($this->prepare_file); 62 | } 63 | if ($this->filesystem->has($this->rename_file)) 64 | { 65 | $this->filesystem->delete($this->rename_file); 66 | } 67 | if ($this->filesystem->has($this->delete_file)) 68 | { 69 | $this->filesystem->delete($this->delete_file); 70 | } 71 | } 72 | 73 | /** 74 | * 75 | */ 76 | public function testPutFile() 77 | { 78 | $tmpfile = tempnam(sys_get_temp_dir(), 'OSS'); 79 | file_put_contents($tmpfile, 'put file'); 80 | 81 | $dest_file = time() . "test-put-file"; 82 | $this->filesystem->putFile($dest_file, $tmpfile); 83 | $this->assertSame('put file', $this->filesystem->read($dest_file)); 84 | 85 | unlink($tmpfile); 86 | $this->filesystem->delete($dest_file); 87 | } 88 | 89 | /** 90 | * 91 | */ 92 | public function testWrite() 93 | { 94 | $dest_file = time() . "test-write-file"; 95 | try 96 | { 97 | $this->filesystem->write($dest_file, '123'); 98 | } catch (OssException $e) { 99 | $this->assertTrue(false); 100 | } 101 | try 102 | { 103 | $this->filesystem->delete($dest_file); 104 | } catch (OssException $e) { 105 | $this->assertTrue(false); 106 | } 107 | } 108 | 109 | /** 110 | * 111 | */ 112 | public function testWriteStream() 113 | { 114 | $dest_file = time() . "test-write-stream"; 115 | $stream = tmpfile(); 116 | fwrite($stream, 'OSS text'); 117 | rewind($stream); 118 | 119 | try 120 | { 121 | $this->filesystem->writeStream($dest_file, $stream); 122 | } catch (OssException $e) { 123 | $this->assertTrue(false); 124 | } 125 | 126 | fclose($stream); 127 | 128 | try 129 | { 130 | $this->filesystem->delete($dest_file); 131 | } catch (OssException $e) { 132 | $this->assertTrue(false); 133 | } 134 | } 135 | 136 | /** 137 | * 138 | */ 139 | public function testUpdate() 140 | { 141 | try 142 | { 143 | $this->filesystem->update($this->prepare_file, __FUNCTION__); 144 | } catch (OssException $e) { 145 | $this->assertTrue(false); 146 | } 147 | } 148 | 149 | /** 150 | * 151 | */ 152 | public function testVisibility() 153 | { 154 | $acl = 'private'; 155 | try 156 | { 157 | $this->filesystem->setVisibility($this->prepare_file, $acl); 158 | } catch (OssException $e) { 159 | $this->assertTrue(false); 160 | } 161 | 162 | try 163 | { 164 | $result = $this->filesystem->getVisibility($this->prepare_file); 165 | } catch (OssException $e) { 166 | $this->assertTrue(false); 167 | } 168 | $this->assertEquals($acl, $result); 169 | 170 | $acl = 'public'; 171 | try 172 | { 173 | $this->filesystem->setVisibility($this->prepare_file, $acl); 174 | } catch (OssException $e) { 175 | $this->assertTrue(false); 176 | } 177 | try 178 | { 179 | $result = $this->filesystem->getVisibility($this->prepare_file); 180 | } catch (OssException $e) { 181 | $this->assertTrue(false); 182 | } 183 | $this->assertEquals('public-read', $result); 184 | } 185 | 186 | /** 187 | * 188 | */ 189 | public function testUpdateStream() 190 | { 191 | $stream = tmpfile(); 192 | fwrite($stream, 'OSS text2'); 193 | rewind($stream); 194 | 195 | try { 196 | $this->filesystem->updateStream($this->prepare_file, $stream); 197 | } catch (OssException $e) { 198 | $this->assertTrue(false); 199 | } 200 | 201 | fclose($stream); 202 | } 203 | 204 | public function testHas() 205 | { 206 | $this->assertTrue($this->filesystem->has($this->prepare_file)); 207 | $this->assertFalse($this->filesystem->has($this->prepare_file . "xxx")); 208 | } 209 | 210 | /** 211 | * 212 | */ 213 | public function testCopy() 214 | { 215 | try 216 | { 217 | $this->filesystem->copy($this->rename_file, 'copy.txt'); 218 | } catch (OssException $e) { 219 | $this->assertTrue(false); 220 | } 221 | $this->assertTrue($this->filesystem->has('copy.txt')); 222 | try 223 | { 224 | $this->filesystem->delete('copy.txt'); 225 | } catch (OssException $e) { 226 | $this->assertTrue(false); 227 | } 228 | } 229 | 230 | /** 231 | * 232 | */ 233 | public function testDelete() 234 | { 235 | try 236 | { 237 | $this->filesystem->delete($this->delete_file); 238 | } catch (OssException $e) { 239 | $this->assertTrue(false); 240 | } 241 | $this->assertFalse($this->filesystem->has($this->delete_file)); 242 | } 243 | 244 | /** 245 | * 246 | */ 247 | public function testRename() 248 | { 249 | $file = time() . 'txt'; 250 | $this->filesystem->rename($this->rename_file, $file); 251 | $this->assertFalse($this->filesystem->has($this->rename_file)); 252 | $this->assertTrue($this->filesystem->has($file)); 253 | $this->filesystem->delete($file); 254 | } 255 | 256 | /** 257 | * 258 | */ 259 | public function testCreateDir() 260 | { 261 | try 262 | { 263 | $this->filesystem->createDir($this->create_dir); 264 | } catch (OssException $e) { 265 | $this->assertTrue(false); 266 | } 267 | try 268 | { 269 | $this->filesystem->copy($this->prepare_file, $this->create_dir . '/' . $this->prepare_file); 270 | } catch(OssException $e) { 271 | $this->assertTrue(false); 272 | } 273 | } 274 | 275 | public function testListContents() 276 | { 277 | $dir = time() ."listcontents"; 278 | $this->filesystem->createDir($dir); 279 | 280 | $this->filesystem->write($dir . '/1.txt', '123'); 281 | $this->filesystem->write($dir . '/2.txt', '123'); 282 | $this->filesystem->write($dir . '/3.txt', '123'); 283 | $this->filesystem->write($dir . '/secondlevel/4.txt', '123'); 284 | $this->filesystem->write($dir . '/secondlevel/5.txt', '123'); 285 | 286 | $list = $this->filesystem->listContents($dir, true); 287 | $this->assertEquals(count($list), 5); 288 | 289 | $this->assertEquals($list[0]['path'], $dir . '/1.txt'); 290 | $this->assertEquals($list[1]['path'], $dir . '/2.txt'); 291 | $this->assertEquals($list[2]['path'], $dir . '/3.txt'); 292 | $this->assertEquals($list[3]['path'], $dir . '/secondlevel/4.txt'); 293 | $this->assertEquals($list[4]['path'], $dir . '/secondlevel/5.txt'); 294 | 295 | $list = $this->filesystem->listContents($dir, false); 296 | $this->assertEquals(count($list), 4); 297 | 298 | $this->assertEquals($list[0]['path'], $dir . '/1.txt'); 299 | $this->assertEquals($list[1]['path'], $dir . '/2.txt'); 300 | $this->assertEquals($list[2]['path'], $dir . '/3.txt'); 301 | $this->assertEquals($list[3]['path'], $dir . '/secondlevel'); 302 | 303 | $this->filesystem->deleteDir($dir); 304 | } 305 | 306 | /** 307 | * 308 | */ 309 | public function testDeleteDir() 310 | { 311 | $this->filesystem->createDir($this->create_dir); 312 | $this->filesystem->deleteDir($this->create_dir); 313 | $this->assertFalse($this->filesystem->has($this->create_dir . '/')); 314 | } 315 | 316 | public function testRead() 317 | { 318 | $this->assertInternalType('string', $this->filesystem->read($this->prepare_file)); 319 | } 320 | 321 | public function testReadStream() 322 | { 323 | $this->assertInternalType('resource', $this->filesystem->readStream($this->prepare_file)); 324 | } 325 | 326 | public function testGetMetadata() 327 | { 328 | $data = $this->filesystem->getMetadata($this->prepare_file); 329 | 330 | $this->assertArrayHasKey('type', $data); 331 | $this->assertArrayHasKey('dirname', $data); 332 | $this->assertArrayHasKey('path', $data); 333 | $this->assertArrayHasKey('timestamp', $data); 334 | $this->assertArrayHasKey('mimetype', $data); 335 | $this->assertArrayHasKey('size', $data); 336 | } 337 | 338 | public function testGetMimetype() 339 | { 340 | $this->assertInternalType('string', $this->filesystem->getMimetype($this->prepare_file)); 341 | } 342 | 343 | public function testGetTimestamp() 344 | { 345 | $this->assertInternalType('integer', $this->filesystem->getTimestamp($this->prepare_file)); 346 | } 347 | 348 | public function testGetSize() 349 | { 350 | $this->assertInternalType('integer', $this->filesystem->getSize($this->prepare_file)); 351 | } 352 | } 353 | --------------------------------------------------------------------------------