Repository: jacobcyl/Aliyun-oss-storage Branch: master Commit: 9abd13bfb288 Files: 8 Total size: 27.7 KB Directory structure: gitextract_pb91tb4y/ ├── LICENSE ├── composer.json ├── readme.md └── src/ ├── AliOssAdapter.php ├── AliOssServiceProvider.php ├── Plugins/ │ ├── PutFile.php │ └── PutRemoteFile.php └── config/ └── config.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2016 jacob 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: composer.json ================================================ { "name": "jacobcyl/ali-oss-storage", "description": "aliyun oss filesystem storage for laravel 5+", "keywords": ["aliyun", "oss", "laravel", "filesystems", "storage"], "homepage": "http://jacobcyl.github.io/Aliyun-oss-storage/", "license": "MIT", "authors": [ { "name": "jacobcyl", "email": "cyl.jacob@gmail.com" } ], "require": { "aliyuncs/oss-sdk-php": "~2.0" }, "autoload": { "psr-4": { "Jacobcyl\\AliOSS\\": "src/" } }, "extra": { "laravel": { "providers": [ "Jacobcyl\\AliOSS\\AliOssServiceProvider" ] } } } ================================================ FILE: readme.md ================================================ # Aliyun-oss-storage for Laravel 5+ Aliyun oss filesystem storage adapter for laravel 5. You can use Aliyun OSS just like laravel Storage as usual. 借鉴了一些优秀的代码,综合各方,同时做了更多优化,将会添加更多完善的接口和插件,打造Laravel最好的OSS Storage扩展 ## Inspired By - [thephpleague/flysystem-aws-s3-v2](https://github.com/thephpleague/flysystem-aws-s3-v2) - [apollopy/flysystem-aliyun-oss](https://github.com/apollopy/flysystem-aliyun-oss) ## Require - Laravel 5+ - cURL extension ##Installation In order to install AliOSS-storage, just add "jacobcyl/ali-oss-storage": "^2.1" to your composer.json. Then run `composer install` or `composer update`. Or you can simply run below command to install: "composer require jacobcyl/ali-oss-storage:^2.1" Then in your `config/app.php` add this line to providers array: ```php Jacobcyl\AliOSS\AliOssServiceProvider::class, ``` ## Configuration Add the following in app/filesystems.php: ```php 'disks'=>[ ... 'oss' => [ 'driver' => 'oss', 'access_id' => '', 'access_key' => '', 'bucket' => '', 'endpoint'     => '', // OSS 外网节点或自定义外部域名            //'endpoint_internal' => '', // v2.0.4 新增配置属性,如果为空,则默认使用 endpoint 配置(由于内网上传有点小问题未解决,请大家暂时不要使用内网节点上传,正在与阿里技术沟通中) 'cdnDomain'     => '', // 如果isCName为true, getUrl会判断cdnDomain是否设定来决定返回的url,如果cdnDomain未设置,则使用endpoint来生成url,否则使用cdn 'ssl'           => // true to use 'https://' and false to use 'http://'. default is false, 'isCName'       => // 是否使用自定义域名,true: 则Storage.url()会使用自定义的cdn或域名生成文件url, false: 则使用外部节点生成url 'debug'         => ], ... ] ``` Then set the default driver in app/filesystems.php: ```php 'default' => 'oss', ``` Ok, well! You are finish to configure. Just feel free to use Aliyun OSS like Storage! ## Usage See [Larave doc for Storage](https://laravel.com/docs/5.2/filesystem#custom-filesystems) Or you can learn here: > First you must use Storage facade ```php use Illuminate\Support\Facades\Storage; ``` > Then You can use all APIs of laravel Storage ```php Storage::disk('oss'); // if default filesystems driver is oss, you can skip this step //fetch all files of specified bucket(see upond configuration) Storage::files($directory); Storage::allFiles($directory); Storage::put('path/to/file/file.jpg', $contents); //first parameter is the target file path, second paramter is file content Storage::putFile('path/to/file/file.jpg', 'local/path/to/local_file.jpg'); // upload file from local path Storage::get('path/to/file/file.jpg'); // get the file object by path Storage::exists('path/to/file/file.jpg'); // determine if a given file exists on the storage(OSS) Storage::size('path/to/file/file.jpg'); // get the file size (Byte) Storage::lastModified('path/to/file/file.jpg'); // get date of last modification Storage::directories($directory); // Get all of the directories within a given directory Storage::allDirectories($directory); // Get all (recursive) of the directories within a given directory Storage::copy('old/file1.jpg', 'new/file1.jpg'); Storage::move('old/file1.jpg', 'new/file1.jpg'); Storage::rename('path/to/file1.jpg', 'path/to/file2.jpg'); Storage::prepend('file.log', 'Prepended Text'); // Prepend to a file. Storage::append('file.log', 'Appended Text'); // Append to a file. Storage::delete('file.jpg'); Storage::delete(['file1.jpg', 'file2.jpg']); Storage::makeDirectory($directory); // Create a directory. Storage::deleteDirectory($directory); // Recursively delete a directory.It will delete all files within a given directory, SO Use with caution please. // upgrade logs // new plugin for v2.0 version Storage::putRemoteFile('target/path/to/file/jacob.jpg', 'http://example.com/jacob.jpg'); //upload remote file to storage by remote url // new function for v2.0.1 version Storage::url('path/to/img.jpg') // get the file url ``` ## Documentation More development detail see [Aliyun OSS DOC](https://help.aliyun.com/document_detail/32099.html?spm=5176.doc31981.6.335.eqQ9dM) ## License Source code is release under MIT license. Read LICENSE file for more information. ================================================ FILE: src/AliOssAdapter.php ================================================ 'raw_contents', 'Content-Length' => 'size', 'ContentType' => 'mimetype', 'Size' => 'size', 'StorageClass' => 'storage_class', ]; /** * @var array */ protected static $metaOptions = [ 'CacheControl', 'Expires', 'ServerSideEncryption', 'Metadata', 'ACL', 'ContentType', 'ContentDisposition', 'ContentLanguage', 'ContentEncoding', ]; protected static $metaMap = [ 'CacheControl' => 'Cache-Control', 'Expires' => 'Expires', 'ServerSideEncryption' => 'x-oss-server-side-encryption', 'Metadata' => 'x-oss-metadata-directive', 'ACL' => 'x-oss-object-acl', 'ContentType' => 'Content-Type', 'ContentDisposition' => 'Content-Disposition', 'ContentLanguage' => 'response-content-language', 'ContentEncoding' => 'Content-Encoding', ]; //Aliyun OSS Client OssClient protected $client; //bucket name protected $bucket; protected $endPoint; protected $cdnDomain; protected $ssl; protected $isCname; //配置 protected $options = [ 'Multipart' => 128 ]; /** * AliOssAdapter constructor. * * @param OssClient $client * @param string $bucket * @param string $endPoint * @param bool $ssl * @param bool $isCname * @param bool $debug * @param null $prefix * @param array $options */ public function __construct( OssClient $client, $bucket, $endPoint, $ssl, $isCname = false, $debug = false, $cdnDomain, $prefix = null, array $options = [] ) { $this->debug = $debug; $this->client = $client; $this->bucket = $bucket; $this->setPathPrefix($prefix); $this->endPoint = $endPoint; $this->ssl = $ssl; $this->isCname = $isCname; $this->cdnDomain = $cdnDomain; $this->options = array_merge($this->options, $options); } /** * Get the OssClient bucket. * * @return string */ public function getBucket() { return $this->bucket; } /** * Get the OSSClient instance. * * @return OssClient */ public function getClient() { return $this->client; } /** * Write a new file. * * @param string $path * @param string $contents * @param Config $config Config object * * @return array|false false on failure file meta data on success */ public function write($path, $contents, Config $config) { $object = $this->applyPathPrefix($path); $options = $this->getOptions($this->options, $config); if (! isset($options[OssClient::OSS_LENGTH])) { $options[OssClient::OSS_LENGTH] = Util::contentSize($contents); } if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, $contents); } try { $this->client->putObject($this->bucket, $object, $contents, $options); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return $this->normalizeResponse($options, $path); } /** * Write a new file using a stream. * * @param string $path * @param resource $resource * @param Config $config Config object * * @return array|false false on failure file meta data on success */ public function writeStream($path, $resource, Config $config) { $options = $this->getOptions($this->options, $config); $contents = stream_get_contents($resource); return $this->write($path, $contents, $config); } public function writeFile($path, $filePath, Config $config){ $object = $this->applyPathPrefix($path); $options = $this->getOptions($this->options, $config); $options[OssClient::OSS_CHECK_MD5] = true; if (! isset($options[OssClient::OSS_CONTENT_TYPE])) { $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, ''); } try { $this->client->uploadFile($this->bucket, $object, $filePath, $options); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return $this->normalizeResponse($options, $path); } /** * Update a file. * * @param string $path * @param string $contents * @param Config $config Config object * * @return array|false false on failure file meta data on success */ public function update($path, $contents, Config $config) { if (! $config->has('visibility') && ! $config->has('ACL')) { $config->set(static::$metaMap['ACL'], $this->getObjectACL($path)); } // $this->delete($path); return $this->write($path, $contents, $config); } /** * Update a file using a stream. * * @param string $path * @param resource $resource * @param Config $config Config object * * @return array|false false on failure file meta data on success */ public function updateStream($path, $resource, Config $config) { $contents = stream_get_contents($resource); return $this->update($path, $contents, $config); } /** * {@inheritdoc} */ public function rename($path, $newpath) { if (! $this->copy($path, $newpath)){ return false; } return $this->delete($path); } /** * {@inheritdoc} */ public function copy($path, $newpath) { $object = $this->applyPathPrefix($path); $newObject = $this->applyPathPrefix($newpath); try{ $this->client->copyObject($this->bucket, $object, $this->bucket, $newObject); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return true; } /** * {@inheritdoc} */ public function delete($path) { $bucket = $this->bucket; $object = $this->applyPathPrefix($path); try{ $this->client->deleteObject($bucket, $object); }catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return ! $this->has($path); } /** * {@inheritdoc} */ public function deleteDir($dirname) { $dirname = rtrim($this->applyPathPrefix($dirname), '/').'/'; $dirObjects = $this->listDirObjects($dirname, true); if(count($dirObjects['objects']) > 0 ){ foreach($dirObjects['objects'] as $object) { $objects[] = $object['Key']; } try { $this->client->deleteObjects($this->bucket, $objects); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } } try { $this->client->deleteObject($this->bucket, $dirname); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return true; } /** * 列举文件夹内文件列表;可递归获取子文件夹; * @param string $dirname 目录 * @param bool $recursive 是否递归 * @return mixed * @throws OssException */ public function listDirObjects($dirname = '', $recursive = false) { $delimiter = '/'; $nextMarker = ''; $maxkeys = 1000; //存储结果 $result = []; while(true){ $options = [ 'delimiter' => $delimiter, 'prefix' => $dirname, 'max-keys' => $maxkeys, 'marker' => $nextMarker, ]; try { $listObjectInfo = $this->client->listObjects($this->bucket, $options); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); // return false; throw $e; } $nextMarker = $listObjectInfo->getNextMarker(); // 得到nextMarker,从上一次listObjects读到的最后一个文件的下一个文件开始继续获取文件列表 $objectList = $listObjectInfo->getObjectList(); // 文件列表 $prefixList = $listObjectInfo->getPrefixList(); // 目录列表 if (!empty($objectList)) { foreach ($objectList as $objectInfo) { $object['Prefix'] = $dirname; $object['Key'] = $objectInfo->getKey(); $object['LastModified'] = $objectInfo->getLastModified(); $object['eTag'] = $objectInfo->getETag(); $object['Type'] = $objectInfo->getType(); $object['Size'] = $objectInfo->getSize(); $object['StorageClass'] = $objectInfo->getStorageClass(); $result['objects'][] = $object; } }else{ $result["objects"] = []; } if (!empty($prefixList)) { foreach ($prefixList as $prefixInfo) { $result['prefix'][] = $prefixInfo->getPrefix(); } }else{ $result['prefix'] = []; } //递归查询子目录所有文件 if($recursive){ foreach( $result['prefix'] as $pfix){ $next = $this->listDirObjects($pfix , $recursive); $result["objects"] = array_merge($result['objects'], $next["objects"]); } } //没有更多结果了 if ($nextMarker === '') { break; } } return $result; } /** * {@inheritdoc} */ public function createDir($dirname, Config $config) { $object = $this->applyPathPrefix($dirname); $options = $this->getOptionsFromConfig($config); try { $this->client->createObjectDir($this->bucket, $object, $options); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return ['path' => $dirname, 'type' => 'dir']; } /** * {@inheritdoc} */ public function setVisibility($path, $visibility) { $object = $this->applyPathPrefix($path); $acl = ( $visibility === AdapterInterface::VISIBILITY_PUBLIC ) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE; $this->client->putObjectAcl($this->bucket, $object, $acl); return compact('visibility'); } /** * {@inheritdoc} */ public function has($path) { $object = $this->applyPathPrefix($path); return $this->client->doesObjectExist($this->bucket, $object); } /** * {@inheritdoc} */ public function read($path) { $result = $this->readObject($path); $result['contents'] = (string) $result['raw_contents']; unset($result['raw_contents']); return $result; } /** * {@inheritdoc} */ public function readStream($path) { $result = $this->readObject($path); $result['stream'] = $result['raw_contents']; rewind($result['stream']); // Ensure the EntityBody object destruction doesn't close the stream $result['raw_contents']->detachStream(); unset($result['raw_contents']); return $result; } /** * Read an object from the OssClient. * * @param string $path * * @return array */ protected function readObject($path) { $object = $this->applyPathPrefix($path); $result['Body'] = $this->client->getObject($this->bucket, $object); $result = array_merge($result, ['type' => 'file']); return $this->normalizeResponse($result, $path); } /** * {@inheritdoc} */ public function listContents($directory = '', $recursive = false) { $dirObjects = $this->listDirObjects($directory, true); $contents = $dirObjects["objects"]; $result = array_map([$this, 'normalizeResponse'], $contents); $result = array_filter($result, function ($value) { return $value['path'] !== false; }); return Util::emulateDirectories($result); } /** * {@inheritdoc} */ public function getMetadata($path) { $object = $this->applyPathPrefix($path); try { $objectMeta = $this->client->getObjectMeta($this->bucket, $object); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } return $objectMeta; } /** * {@inheritdoc} */ public function getSize($path) { $object = $this->getMetadata($path); $object['size'] = $object['content-length']; return $object; } /** * {@inheritdoc} */ public function getMimetype($path) { if( $object = $this->getMetadata($path)) $object['mimetype'] = $object['content-type']; return $object; } /** * {@inheritdoc} */ public function getTimestamp($path) { if( $object = $this->getMetadata($path)) $object['timestamp'] = strtotime( $object['last-modified'] ); return $object; } /** * {@inheritdoc} */ public function getVisibility($path) { $object = $this->applyPathPrefix($path); try { $acl = $this->client->getObjectAcl($this->bucket, $object); } catch (OssException $e) { $this->logErr(__FUNCTION__, $e); return false; } if ($acl == OssClient::OSS_ACL_TYPE_PUBLIC_READ ){ $res['visibility'] = AdapterInterface::VISIBILITY_PUBLIC; }else{ $res['visibility'] = AdapterInterface::VISIBILITY_PRIVATE; } return $res; } /** * @param $path * * @return string */ public function getUrl( $path ) { if (!$this->has($path)) throw new FileNotFoundException($filePath.' not found'); return ( $this->ssl ? 'https://' : 'http://' ) . ( $this->isCname ? ( $this->cdnDomain == '' ? $this->endPoint : $this->cdnDomain ) : $this->bucket . '.' . $this->endPoint ) . '/' . ltrim($path, '/'); } /** * The the ACL visibility. * * @param string $path * * @return string */ protected function getObjectACL($path) { $metadata = $this->getVisibility($path); return $metadata['visibility'] === AdapterInterface::VISIBILITY_PUBLIC ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE; } /** * Normalize a result from OSS. * * @param array $object * @param string $path * * @return array file metadata */ protected function normalizeResponse(array $object, $path = null) { $result = ['path' => $path ?: $this->removePathPrefix(isset($object['Key']) ? $object['Key'] : $object['Prefix'])]; $result['dirname'] = Util::dirname($result['path']); if (isset($object['LastModified'])) { $result['timestamp'] = strtotime($object['LastModified']); } if (substr($result['path'], -1) === '/') { $result['type'] = 'dir'; $result['path'] = rtrim($result['path'], '/'); return $result; } $result = array_merge($result, Util::map($object, static::$resultMap), ['type' => 'file']); return $result; } /** * Get options for a OSS call. done * * @param array $options * * @return array OSS options */ protected function getOptions(array $options = [], Config $config = null) { $options = array_merge($this->options, $options); if ($config) { $options = array_merge($options, $this->getOptionsFromConfig($config)); } return array(OssClient::OSS_HEADERS => $options); } /** * Retrieve options from a Config instance. done * * @param Config $config * * @return array */ protected function getOptionsFromConfig(Config $config) { $options = []; foreach (static::$metaOptions as $option) { if (! $config->has($option)) { continue; } $options[static::$metaMap[$option]] = $config->get($option); } if ($visibility = $config->get('visibility')) { // For local reference // $options['visibility'] = $visibility; // For external reference $options['x-oss-object-acl'] = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE; } if ($mimetype = $config->get('mimetype')) { // For local reference // $options['mimetype'] = $mimetype; // For external reference $options['Content-Type'] = $mimetype; } return $options; } /** * @param $fun string function name : __FUNCTION__ * @param $e */ protected function logErr($fun, $e){ if( $this->debug ){ Log::error($fun . ": FAILED"); Log::error($e->getMessage()); } } } ================================================ FILE: src/AliOssServiceProvider.php ================================================ publishes([ __DIR__ . '/config/config.php' => config_path('alioss.php'), ], 'config'); } */ Storage::extend('oss', function($app, $config) { $accessId = $config['access_id']; $accessKey = $config['access_key']; $cdnDomain = empty($config['cdnDomain']) ? '' : $config['cdnDomain']; $bucket = $config['bucket']; $ssl = empty($config['ssl']) ? false : $config['ssl']; $isCname = empty($config['isCName']) ? false : $config['isCName']; $debug = empty($config['debug']) ? false : $config['debug']; $endPoint = $config['endpoint']; // 默认作为外部节点 $epInternal= $isCname?$cdnDomain:(empty($config['endpoint_internal']) ? $endPoint : $config['endpoint_internal']); // 内部节点 if($debug) Log::debug('OSS config:', $config); $client = new OssClient($accessId, $accessKey, $epInternal, $isCname); $adapter = new AliOssAdapter($client, $bucket, $endPoint, $ssl, $isCname, $debug, $cdnDomain); //Log::debug($client); $filesystem = new Filesystem($adapter); $filesystem->addPlugin(new PutFile()); $filesystem->addPlugin(new PutRemoteFile()); //$filesystem->addPlugin(new CallBack()); return $filesystem; }); } /** * Register the application services. * * @return void */ public function register() { } } ================================================ FILE: src/Plugins/PutFile.php ================================================ filesystem, 'getConfig')) { $config->setFallback($this->filesystem->getConfig()); } return (bool)$this->filesystem->getAdapter()->writeFile($path, $filePath, $config); } } ================================================ FILE: src/Plugins/PutRemoteFile.php ================================================ filesystem, 'getConfig')) { $config->setFallback($this->filesystem->getConfig()); } //Get file stream from remote url $resource = fopen($remoteUrl, 'r'); return (bool)$this->filesystem->getAdapter()->writeStream($path, $resource, $config); } } ================================================ FILE: src/config/config.php ================================================ true, ];