├── _config.yml ├── .gitignore ├── .styleci.yml ├── composer.json ├── phpunit.xml.dist ├── CHANGELOG.md ├── LICENSE.md ├── _ide_helper.php ├── src ├── Plugins │ ├── SignedDownloadUrl.php │ └── PutFile.php ├── AliyunOssServiceProvider.php └── AliyunOssAdapter.php ├── README.md ├── .php_cs └── tests └── AliyunOssAdapterTest.php /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | finder: 3 | not-name: 4 | - "_ide_helper.php" 5 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollopy/flysystem-aliyun-oss", 3 | "description": "This is a Flysystem adapter for the Aliyun OSS ~2.3", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "ApolloPY", 8 | "email": "ApolloPY@Gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": ">=5.5.9", 13 | "league/flysystem": "~1.0", 14 | "aliyuncs/oss-sdk-php": "~2.3" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "~4.0" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "ApolloPY\\Flysystem\\AliyunOss\\": "src/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | 21 | ./src/ 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## 1.1.3 4 | 5 | * 支持 config/filesystems.php 里配置目录前缀 (使用场景: 希望开发环境下上传的文件不要覆盖业务环境下上传的文件) 6 | 7 | ## 1.1.2 8 | 9 | * Plugin/PrivateDownloadUrl 改为 Plugin/SignedDownloadUrl, 获得带签名且有过期时间的下载地址, 支持自定义 hostname 和选择是否使用 ssl (使用场景: 远程配置上传地址为内网, 但需要获得外网的临时下载地址提供给用户) 10 | 11 | ## 1.1.1 12 | 13 | * ~~添加 Plugin/PrivateDownloadUrl, 获得私有下载地址~~ **(该方法设计不成熟, 下一版本就改掉了, 如要使用此功能, 建议升级 1.1.2)** 14 | * 添加 _ide_helper.php, 让 FilesystemAdapter __call 调用 Plugin 时, IDE 有代码提示 15 | 16 | ## 1.1.0 17 | 18 | * 添加 Plugin/PutFile 方法, 支持指定本地文件路径上传到 OSS 19 | * AliyunOssAdapter 支持外部传入 mimetype 和 size 两个参数 20 | 21 | ## 1.0.0 22 | 23 | * AliyunOssAdapter 实现 League\Flysystem\Adapter\AbstractAdapter 定义的所有接口 24 | * 添加 AliyunOssServiceProvider 用于 Laravel 注册 25 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | #The MIT License (MIT) 2 | 3 | Copyright (c) ApolloPY 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. 22 | -------------------------------------------------------------------------------- /_ide_helper.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class SignedDownloadUrl extends AbstractPlugin 14 | { 15 | /** 16 | * Get the method name. 17 | * 18 | * @return string 19 | */ 20 | public function getMethod() 21 | { 22 | return 'signedDownloadUrl'; 23 | } 24 | 25 | /** 26 | * Handle. 27 | * 28 | * @param string $path 29 | * @param int $expires 30 | * @param string $host_name 31 | * @param bool $use_ssl 32 | * @return string|false 33 | */ 34 | public function handle($path, $expires = 3600, $host_name = '', $use_ssl = false) 35 | { 36 | if (! method_exists($this->filesystem, 'getAdapter')) { 37 | return false; 38 | } 39 | 40 | if (! method_exists($this->filesystem->getAdapter(), 'getSignedDownloadUrl')) { 41 | return false; 42 | } 43 | 44 | return $this->filesystem->getAdapter()->getSignedDownloadUrl($path, $expires, $host_name, $use_ssl); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/AliyunOssServiceProvider.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | class AliyunOssServiceProvider extends ServiceProvider 18 | { 19 | /** 20 | * Perform post-registration booting of services. 21 | * 22 | * @return void 23 | */ 24 | public function boot() 25 | { 26 | Storage::extend('oss', function ($app, $config) { 27 | $accessId = $config['access_id']; 28 | $accessKey = $config['access_key']; 29 | $endPoint = $config['endpoint']; 30 | $bucket = $config['bucket']; 31 | 32 | $prefix = null; 33 | if (isset($config['prefix'])) { 34 | $prefix = $config['prefix']; 35 | } 36 | 37 | $client = new OssClient($accessId, $accessKey, $endPoint); 38 | $adapter = new AliyunOssAdapter($client, $bucket, $prefix); 39 | 40 | $filesystem = new Filesystem($adapter); 41 | $filesystem->addPlugin(new PutFile()); 42 | $filesystem->addPlugin(new SignedDownloadUrl()); 43 | 44 | return $filesystem; 45 | }); 46 | } 47 | 48 | /** 49 | * Register bindings in the container. 50 | * 51 | * @return void 52 | */ 53 | public function register() 54 | { 55 | // 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flysystem Adapter for Aliyun OSS. 2 | 3 | This is a Flysystem adapter for the Aliyun OSS ~2.3 4 | 5 | inspire by [aobozhang/aliyun-oss-adapter](https://github.com/aobozhang/aliyun-oss-adapter) 6 | 7 | ## Installation 8 | 9 | ```bash 10 | composer require apollopy/flysystem-aliyun-oss 11 | ``` 12 | 13 | ## for Laravel 14 | 15 | This service provider must be registered. 16 | 17 | ```php 18 | // config/app.php 19 | 20 | 'providers' => [ 21 | '...', 22 | ApolloPY\Flysystem\AliyunOss\AliyunOssServiceProvider::class, 23 | ]; 24 | ``` 25 | 26 | edit the config file: config/filesystems.php 27 | 28 | add config 29 | 30 | ```php 31 | 'oss' => [ 32 | 'driver' => 'oss', 33 | 'access_id' => env('OSS_ACCESS_ID','your id'), 34 | 'access_key' => env('OSS_ACCESS_KEY','your key'), 35 | 'bucket' => env('OSS_BUCKET','your bucket'), 36 | 'endpoint' => env('OSS_ENDPOINT','your endpoint'), 37 | 'prefix' => env('OSS_PREFIX', ''), // optional 38 | ], 39 | ``` 40 | 41 | change default to oss 42 | 43 | ```php 44 | 'default' => 'oss' 45 | ``` 46 | 47 | ## Use 48 | 49 | see [Laravel wiki](https://laravel.com/docs/5.1/filesystem) 50 | 51 | ## Plugins 52 | 53 | inspire by [itbdw/laravel-storage-qiniu](https://github.com/itbdw/laravel-storage-qiniu) 54 | 55 | ```php 56 | Storage::disk('oss')->putFile($md5_path, '/local_file_path/1.png', ['mimetype' => 'image/png','filename' => 'filename_by_down.png']); 57 | 58 | Storage::disk('oss')->signedDownloadUrl($path, 3600, 'oss-cn-beijing.aliyuncs.com', true); 59 | ``` 60 | 61 | ## IDE Helper 62 | 63 | if installed [barryvdh/laravel-ide-helper](https://github.com/barryvdh/laravel-ide-helper) 64 | 65 | edit the config file: config/ide-helper.php 66 | 67 | ```php 68 | 'interfaces' => [ 69 | '\Illuminate\Contracts\Filesystem\Filesystem' => ApolloPY\Flysystem\AliyunOss\FilesystemAdapter::class, 70 | ], 71 | ``` 72 | -------------------------------------------------------------------------------- /.php_cs: -------------------------------------------------------------------------------- 1 | in(__DIR__) 9 | ; 10 | 11 | $fixers = ' 12 | -psr0 13 | align_double_arrow 14 | class_definition 15 | new_with_braces 16 | no_closing_tag 17 | ordered_imports 18 | phpdoc_align 19 | 20 | binary_operator_spaces 21 | blank_line_after_namespace 22 | blank_line_after_opening_tag 23 | blank_line_before_return 24 | braces 25 | cast_spaces 26 | concat_without_spaces 27 | elseif 28 | encoding 29 | full_opening_tag 30 | function_declaration 31 | function_typehint_space 32 | hash_to_slash_comment 33 | heredoc_to_nowdoc 34 | include 35 | lowercase_cast 36 | lowercase_constants 37 | lowercase_keywords 38 | method_argument_space 39 | method_separation 40 | native_function_casing 41 | no_alias_functions 42 | no_blank_lines_after_class_opening 43 | no_blank_lines_after_phpdoc 44 | no_blank_lines_between_uses 45 | no_duplicate_semicolons 46 | no_empty_phpdoc 47 | no_extra_consecutive_blank_lines 48 | no_leading_import_slash 49 | no_leading_namespace_whitespace 50 | no_multiline_whitespace_around_double_arrow 51 | no_multiline_whitespace_before_semicolons 52 | no_short_bool_cast 53 | no_singleline_whitespace_before_semicolons 54 | no_spaces_after_function_name 55 | no_spaces_inside_parenthesis 56 | no_tab_indentation 57 | no_trailing_comma_in_list_call 58 | no_trailing_comma_in_singleline_array 59 | no_trailing_whitespace 60 | no_trailing_whitespace_in_comment 61 | no_unneeded_control_parentheses 62 | no_unreachable_default_argument_value 63 | no_unused_imports 64 | no_useless_return 65 | no_whitespace_before_comma_in_array 66 | no_whitespace_in_blank_lines 67 | not_operator_with_successor_space 68 | object_operator_without_whitespace 69 | phpdoc_indent 70 | phpdoc_inline_tag 71 | phpdoc_no_access 72 | phpdoc_no_package 73 | phpdoc_scalar 74 | phpdoc_single_line_var_spacing 75 | phpdoc_summary 76 | phpdoc_to_comment 77 | phpdoc_trim 78 | phpdoc_type_to_var 79 | phpdoc_types 80 | phpdoc_var_without_name 81 | print_to_echo 82 | psr4 83 | self_accessor 84 | short_array_syntax 85 | short_scalar_cast 86 | simplified_null_return 87 | single_blank_line_at_eof 88 | single_blank_line_before_namespace 89 | single_import_per_statement 90 | single_line_after_imports 91 | single_quote 92 | space_after_semicolon 93 | standardize_not_equals 94 | switch_case_semicolon_to_colon 95 | switch_case_space 96 | ternary_operator_spaces 97 | trailing_comma_in_multiline_array 98 | trim_array_spaces 99 | unalign_equals 100 | unary_operator_spaces 101 | unix_line_endings 102 | visibility_required 103 | whitespace_after_comma_in_array 104 | '; 105 | 106 | return Symfony\CS\Config\Config::create() 107 | ->level(Symfony\CS\FixerInterface::PSR2_LEVEL) 108 | ->fixers(array_values(array_filter(explode("\n", $fixers)))) 109 | ->finder($finder); -------------------------------------------------------------------------------- /tests/AliyunOssAdapterTest.php: -------------------------------------------------------------------------------- 1 | deleteDir('test'); 34 | $adapter->setPathPrefix('test'); 35 | 36 | $filesystem = new Filesystem($adapter); 37 | $filesystem->addPlugin(new PutFile()); 38 | 39 | $this->filesystem = $filesystem; 40 | } 41 | 42 | public function testPutFile() 43 | { 44 | $tmpfile = tempnam(sys_get_temp_dir(), 'OSS'); 45 | file_put_contents($tmpfile, 'put file'); 46 | 47 | $this->assertTrue($this->filesystem->putFile('1.txt', $tmpfile)); 48 | $this->assertSame('put file', $this->filesystem->read('1.txt')); 49 | 50 | unlink($tmpfile); 51 | $this->filesystem->delete('1.txt'); 52 | } 53 | 54 | /** 55 | * 1.txt. 56 | */ 57 | public function testWrite() 58 | { 59 | $this->assertTrue($this->filesystem->write('1.txt', '123')); 60 | } 61 | 62 | /** 63 | * 1.txt 64 | * 2.txt. 65 | */ 66 | public function testWriteStream() 67 | { 68 | $stream = tmpfile(); 69 | fwrite($stream, 'OSS text'); 70 | rewind($stream); 71 | 72 | $this->assertTrue($this->filesystem->writeStream('2.txt', $stream)); 73 | 74 | fclose($stream); 75 | } 76 | 77 | /** 78 | * 1.txt 79 | * 2.txt. 80 | */ 81 | public function testUpdate() 82 | { 83 | $this->assertTrue($this->filesystem->update('1.txt', '456')); 84 | } 85 | 86 | /** 87 | * 1.txt 88 | * 2.txt. 89 | */ 90 | public function testUpdateStream() 91 | { 92 | $stream = tmpfile(); 93 | fwrite($stream, 'OSS text2'); 94 | rewind($stream); 95 | 96 | $this->assertTrue($this->filesystem->updateStream('2.txt', $stream)); 97 | 98 | fclose($stream); 99 | } 100 | 101 | public function testHas() 102 | { 103 | $this->assertTrue($this->filesystem->has('1.txt')); 104 | $this->assertFalse($this->filesystem->has('3.txt')); 105 | } 106 | 107 | /** 108 | * 1.txt 109 | * 2.txt 110 | * 3.txt. 111 | */ 112 | public function testCopy() 113 | { 114 | $this->assertTrue($this->filesystem->copy('1.txt', '3.txt')); 115 | $this->assertTrue($this->filesystem->has('3.txt')); 116 | } 117 | 118 | /** 119 | * 1.txt 120 | * 2.txt. 121 | */ 122 | public function testDelete() 123 | { 124 | $this->assertTrue($this->filesystem->delete('3.txt')); 125 | $this->assertFalse($this->filesystem->has('3.txt')); 126 | } 127 | 128 | /** 129 | * 3.txt 130 | * 2.txt. 131 | */ 132 | public function testRename() 133 | { 134 | $this->assertTrue($this->filesystem->rename('1.txt', '3.txt')); 135 | $this->assertFalse($this->filesystem->has('1.txt')); 136 | $this->assertTrue($this->filesystem->has('3.txt')); 137 | } 138 | 139 | /** 140 | * 3.txt 141 | * 2.txt 142 | * test/1.txt. 143 | */ 144 | public function testCreateDir() 145 | { 146 | $this->assertTrue($this->filesystem->createDir('test')); 147 | $this->assertTrue($this->filesystem->copy('2.txt', 'test/1.txt')); 148 | } 149 | 150 | public function testListContents() 151 | { 152 | $list = $this->filesystem->listContents(''); 153 | $this->assertArraySubset( 154 | [ 155 | [ 156 | 'type' => 'file', 157 | 'path' => '2.txt', 158 | ], 159 | [ 160 | 'type' => 'file', 161 | 'path' => '3.txt', 162 | ], 163 | [ 164 | 'type' => 'dir', 165 | 'path' => 'test', 166 | ], 167 | ], 168 | $list 169 | ); 170 | 171 | $list = $this->filesystem->listContents('', true); 172 | $this->assertArraySubset( 173 | [ 174 | [ 175 | 'type' => 'file', 176 | 'path' => '2.txt', 177 | ], 178 | [ 179 | 'type' => 'file', 180 | 'path' => '3.txt', 181 | ], 182 | [ 183 | 'type' => 'dir', 184 | 'path' => 'test', 185 | ], 186 | [ 187 | 'type' => 'file', 188 | 'path' => 'test/1.txt', 189 | ], 190 | ], 191 | $list 192 | ); 193 | } 194 | 195 | /** 196 | * 3.txt 197 | * 2.txt. 198 | */ 199 | public function testDeleteDir() 200 | { 201 | $this->assertTrue($this->filesystem->deleteDir('test')); 202 | $this->assertFalse($this->filesystem->has('test/')); 203 | } 204 | 205 | public function testRead() 206 | { 207 | $this->assertInternalType('string', $this->filesystem->read('2.txt')); 208 | } 209 | 210 | public function testReadStream() 211 | { 212 | $this->assertInternalType('resource', $this->filesystem->readStream('2.txt')); 213 | } 214 | 215 | public function testGetMetadata() 216 | { 217 | $data = $this->filesystem->getMetadata('2.txt'); 218 | 219 | $this->assertArrayHasKey('type', $data); 220 | $this->assertArrayHasKey('dirname', $data); 221 | $this->assertArrayHasKey('path', $data); 222 | $this->assertArrayHasKey('timestamp', $data); 223 | $this->assertArrayHasKey('mimetype', $data); 224 | $this->assertArrayHasKey('size', $data); 225 | } 226 | 227 | public function testGetMimetype() 228 | { 229 | $this->assertInternalType('string', $this->filesystem->getMimetype('2.txt')); 230 | } 231 | 232 | public function testGetTimestamp() 233 | { 234 | $this->assertInternalType('integer', $this->filesystem->getTimestamp('2.txt')); 235 | } 236 | 237 | public function testGetSize() 238 | { 239 | $this->assertInternalType('integer', $this->filesystem->getSize('2.txt')); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/AliyunOssAdapter.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class AliyunOssAdapter extends AbstractAdapter 19 | { 20 | use StreamedTrait; 21 | use NotSupportingVisibilityTrait; 22 | 23 | /** 24 | * Aliyun Oss Client. 25 | * 26 | * @var \OSS\OssClient 27 | */ 28 | protected $client; 29 | 30 | /** 31 | * bucket name. 32 | * 33 | * @var string 34 | */ 35 | protected $bucket; 36 | 37 | /** 38 | * @var array 39 | */ 40 | protected $options = []; 41 | 42 | /** 43 | * @var array 44 | */ 45 | protected static $mappingOptions = [ 46 | 'mimetype' => OssClient::OSS_CONTENT_TYPE, 47 | 'size' => OssClient::OSS_LENGTH, 48 | 'filename' => OssClient::OSS_CONTENT_DISPOSTION, 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 | $options[OssClient::OSS_CHECK_MD5] = true; 101 | 102 | if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { 103 | $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, ''); 104 | } 105 | 106 | try { 107 | $this->client->uploadFile($this->bucket, $object, $localFilePath, $options); 108 | } catch (OssException $e) { 109 | return false; 110 | } 111 | 112 | $type = 'file'; 113 | $result = compact('type', 'path'); 114 | $result['mimetype'] = $options[OssClient::OSS_CONTENT_TYPE]; 115 | 116 | return $result; 117 | } 118 | 119 | /** 120 | * Write a new file. 121 | * 122 | * @param string $path 123 | * @param string $contents 124 | * @param Config $config Config object 125 | * @return array|false false on failure file meta data on success 126 | */ 127 | public function write($path, $contents, Config $config) 128 | { 129 | $object = $this->applyPathPrefix($path); 130 | $options = $this->getOptionsFromConfig($config); 131 | 132 | if (! isset($options[OssClient::OSS_LENGTH])) { 133 | $options[OssClient::OSS_LENGTH] = Util::contentSize($contents); 134 | } 135 | 136 | if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { 137 | $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, $contents); 138 | } 139 | 140 | try { 141 | $this->client->putObject($this->bucket, $object, $contents, $options); 142 | } catch (OssException $e) { 143 | return false; 144 | } 145 | 146 | $type = 'file'; 147 | $result = compact('type', 'path', 'contents'); 148 | $result['mimetype'] = $options[OssClient::OSS_CONTENT_TYPE]; 149 | $result['size'] = $options[OssClient::OSS_LENGTH]; 150 | 151 | return $result; 152 | } 153 | 154 | /** 155 | * Update a file. 156 | * 157 | * @param string $path 158 | * @param string $contents 159 | * @param Config $config Config object 160 | * @return array|false false on failure file meta data on success 161 | */ 162 | public function update($path, $contents, Config $config) 163 | { 164 | return $this->write($path, $contents, $config); 165 | } 166 | 167 | /** 168 | * Rename a file. 169 | * 170 | * @param string $path 171 | * @param string $newpath 172 | * @return bool 173 | */ 174 | public function rename($path, $newpath) 175 | { 176 | if (! $this->copy($path, $newpath)) { 177 | return false; 178 | } 179 | 180 | return $this->delete($path); 181 | } 182 | 183 | /** 184 | * Copy a file. 185 | * 186 | * @param string $path 187 | * @param string $newpath 188 | * @return bool 189 | */ 190 | public function copy($path, $newpath) 191 | { 192 | $object = $this->applyPathPrefix($path); 193 | $newobject = $this->applyPathPrefix($newpath); 194 | 195 | try { 196 | $this->client->copyObject($this->bucket, $object, $this->bucket, $newobject); 197 | } catch (OssException $e) { 198 | return false; 199 | } 200 | 201 | return true; 202 | } 203 | 204 | /** 205 | * Delete a file. 206 | * 207 | * @param string $path 208 | * @return bool 209 | */ 210 | public function delete($path) 211 | { 212 | $object = $this->applyPathPrefix($path); 213 | 214 | try { 215 | $this->client->deleteObject($this->bucket, $object); 216 | } catch (OssException $e) { 217 | return false; 218 | } 219 | 220 | return true; 221 | } 222 | 223 | /** 224 | * Delete a directory. 225 | * 226 | * @param string $dirname 227 | * @return bool 228 | */ 229 | public function deleteDir($dirname) 230 | { 231 | $list = $this->listContents($dirname, true); 232 | 233 | $objects = []; 234 | foreach ($list as $val) { 235 | if ($val['type'] === 'file') { 236 | $objects[] = $this->applyPathPrefix($val['path']); 237 | } else { 238 | $objects[] = $this->applyPathPrefix($val['path']).'/'; 239 | } 240 | } 241 | 242 | try { 243 | $this->client->deleteObjects($this->bucket, $objects); 244 | } catch (OssException $e) { 245 | return false; 246 | } 247 | 248 | return true; 249 | } 250 | 251 | /** 252 | * Create a directory. 253 | * 254 | * @param string $dirname directory name 255 | * @param Config $config 256 | * @return array|false 257 | */ 258 | public function createDir($dirname, Config $config) 259 | { 260 | $object = $this->applyPathPrefix($dirname); 261 | $options = $this->getOptionsFromConfig($config); 262 | 263 | try { 264 | $this->client->createObjectDir($this->bucket, $object, $options); 265 | } catch (OssException $e) { 266 | return false; 267 | } 268 | 269 | return ['path' => $dirname, 'type' => 'dir']; 270 | } 271 | 272 | /** 273 | * Check whether a file exists. 274 | * 275 | * @param string $path 276 | * @return bool 277 | */ 278 | public function has($path) 279 | { 280 | $object = $this->applyPathPrefix($path); 281 | 282 | try { 283 | $exists = $this->client->doesObjectExist($this->bucket, $object); 284 | } catch (OssException $e) { 285 | return false; 286 | } 287 | 288 | return $exists; 289 | } 290 | 291 | /** 292 | * Read a file. 293 | * 294 | * @param string $path 295 | * @return array|false 296 | */ 297 | public function read($path) 298 | { 299 | $object = $this->applyPathPrefix($path); 300 | 301 | try { 302 | $contents = $this->client->getObject($this->bucket, $object); 303 | } catch (OssException $e) { 304 | return false; 305 | } 306 | 307 | return compact('contents', 'path'); 308 | } 309 | 310 | /** 311 | * List contents of a directory. 312 | * 313 | * @param string $directory 314 | * @param bool $recursive 315 | * @return array 316 | */ 317 | public function listContents($directory = '', $recursive = false) 318 | { 319 | $directory = rtrim($this->applyPathPrefix($directory), '\\/'); 320 | if ($directory) $directory.='/'; 321 | 322 | $bucket = $this->bucket; 323 | $delimiter = '/'; 324 | $nextMarker = ''; 325 | $maxkeys = 1000; 326 | $options = [ 327 | 'delimiter' => $delimiter, 328 | 'prefix' => $directory, 329 | 'max-keys' => $maxkeys, 330 | 'marker' => $nextMarker, 331 | ]; 332 | 333 | $listObjectInfo = $this->client->listObjects($bucket, $options); 334 | 335 | $objectList = $listObjectInfo->getObjectList(); // 文件列表 336 | $prefixList = $listObjectInfo->getPrefixList(); // 目录列表 337 | 338 | $result = []; 339 | foreach ($objectList as $objectInfo) { 340 | if ($objectInfo->getSize() === 0 && $directory === $objectInfo->getKey()) { 341 | $result[] = [ 342 | 'type' => 'dir', 343 | 'path' => $this->removePathPrefix(rtrim($objectInfo->getKey(), '/')), 344 | 'timestamp' => strtotime($objectInfo->getLastModified()), 345 | ]; 346 | continue; 347 | } 348 | 349 | $result[] = [ 350 | 'type' => 'file', 351 | 'path' => $this->removePathPrefix($objectInfo->getKey()), 352 | 'timestamp' => strtotime($objectInfo->getLastModified()), 353 | 'size' => $objectInfo->getSize(), 354 | ]; 355 | } 356 | 357 | foreach ($prefixList as $prefixInfo) { 358 | if ($recursive) { 359 | $next = $this->listContents($this->removePathPrefix($prefixInfo->getPrefix()), $recursive); 360 | $result = array_merge($result, $next); 361 | } else { 362 | $result[] = [ 363 | 'type' => 'dir', 364 | 'path' => $this->removePathPrefix(rtrim($prefixInfo->getPrefix(), '/')), 365 | 'timestamp' => 0, 366 | ]; 367 | } 368 | } 369 | 370 | return $result; 371 | } 372 | 373 | /** 374 | * Get all the meta data of a file or directory. 375 | * 376 | * @param string $path 377 | * @return array|false 378 | * @throws \OSS\Core\OssException 379 | */ 380 | public function getMetadata($path) 381 | { 382 | $object = $this->applyPathPrefix($path); 383 | 384 | try { 385 | $result = $this->client->getObjectMeta($this->bucket, $object); 386 | } catch (OssException $e) { 387 | return false; 388 | } 389 | 390 | return [ 391 | 'type' => 'file', 392 | 'dirname' => Util::dirname($path), 393 | 'path' => $path, 394 | 'timestamp' => strtotime($result['last-modified']), 395 | 'mimetype' => $result['content-type'], 396 | 'size' => $result['content-length'], 397 | ]; 398 | } 399 | 400 | /** 401 | * Get all the meta data of a file or directory. 402 | * 403 | * @param string $path 404 | * @return array|false 405 | */ 406 | public function getSize($path) 407 | { 408 | return $this->getMetadata($path); 409 | } 410 | 411 | /** 412 | * Get the mimetype of a file. 413 | * 414 | * @param string $path 415 | * @return array|false 416 | */ 417 | public function getMimetype($path) 418 | { 419 | return $this->getMetadata($path); 420 | } 421 | 422 | /** 423 | * Get the timestamp of a file. 424 | * 425 | * @param string $path 426 | * @return array|false 427 | */ 428 | public function getTimestamp($path) 429 | { 430 | return $this->getMetadata($path); 431 | } 432 | 433 | /** 434 | * Get the signed download url of a file. 435 | * 436 | * @param string $path 437 | * @param int $expires 438 | * @param string $host_name 439 | * @param bool $use_ssl 440 | * @return string 441 | */ 442 | public function getSignedDownloadUrl($path, $expires = 3600, $host_name = '', $use_ssl = false) 443 | { 444 | $object = $this->applyPathPrefix($path); 445 | $url = $this->client->signUrl($this->bucket, $object, $expires); 446 | 447 | if (! empty($host_name) || $use_ssl) { 448 | $parse_url = parse_url($url); 449 | if (! empty($host_name)) { 450 | $parse_url['host'] = $this->bucket.'.'.$host_name; 451 | } 452 | if ($use_ssl) { 453 | $parse_url['scheme'] = 'https'; 454 | } 455 | 456 | $url = (isset($parse_url['scheme']) ? $parse_url['scheme'].'://' : '') 457 | .( 458 | isset($parse_url['user']) ? 459 | $parse_url['user'].(isset($parse_url['pass']) ? ':'.$parse_url['pass'] : '').'@' 460 | : '' 461 | ) 462 | .(isset($parse_url['host']) ? $parse_url['host'] : '') 463 | .(isset($parse_url['port']) ? ':'.$parse_url['port'] : '') 464 | .(isset($parse_url['path']) ? $parse_url['path'] : '') 465 | .(isset($parse_url['query']) ? '?'.$parse_url['query'] : ''); 466 | } 467 | 468 | return $url; 469 | } 470 | 471 | /** 472 | * Get options from the config. 473 | * 474 | * @param Config $config 475 | * @return array 476 | */ 477 | protected function getOptionsFromConfig(Config $config) 478 | { 479 | $options = $this->options; 480 | foreach (static::$mappingOptions as $option => $ossOption) { 481 | if (! $config->has($option)) { 482 | continue; 483 | } 484 | $options[$ossOption] = $config->get($option); 485 | } 486 | 487 | return $options; 488 | } 489 | } 490 | --------------------------------------------------------------------------------