[
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 jacob\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"jacobcyl/ali-oss-storage\",\n    \"description\": \"aliyun oss filesystem storage for laravel 5+\",\n    \"keywords\": [\"aliyun\", \"oss\", \"laravel\", \"filesystems\", \"storage\"],\n    \"homepage\": \"http://jacobcyl.github.io/Aliyun-oss-storage/\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"jacobcyl\",\n            \"email\": \"cyl.jacob@gmail.com\"\n        }\n    ],\n    \"require\": {\n        \"aliyuncs/oss-sdk-php\": \"~2.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"Jacobcyl\\\\AliOSS\\\\\": \"src/\"\n        }\n    },\n    \"extra\": {\n        \"laravel\": {\n            \"providers\": [\n                \"Jacobcyl\\\\AliOSS\\\\AliOssServiceProvider\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "# Aliyun-oss-storage for Laravel 5+\nAliyun oss filesystem storage adapter for laravel 5. You can use Aliyun OSS just like laravel Storage as usual.    \n借鉴了一些优秀的代码，综合各方，同时做了更多优化，将会添加更多完善的接口和插件，打造Laravel最好的OSS Storage扩展\n## Inspired By\n- [thephpleague/flysystem-aws-s3-v2](https://github.com/thephpleague/flysystem-aws-s3-v2)\n- [apollopy/flysystem-aliyun-oss](https://github.com/apollopy/flysystem-aliyun-oss) \n\n## Require\n- Laravel 5+\n- cURL extension\n\n##Installation\nIn order to install AliOSS-storage, just add\n\n    \"jacobcyl/ali-oss-storage\": \"^2.1\"\n\nto your composer.json. Then run `composer install` or `composer update`.  \nOr you can simply run below command to install:\n\n    \"composer require jacobcyl/ali-oss-storage:^2.1\"\n    \nThen in your `config/app.php` add this line to providers array:\n```php\nJacobcyl\\AliOSS\\AliOssServiceProvider::class,\n```\n## Configuration\nAdd the following in app/filesystems.php:\n```php\n'disks'=>[\n    ...\n    'oss' => [\n            'driver'        => 'oss',\n            'access_id'     => '<Your Aliyun OSS AccessKeyId>',\n            'access_key'    => '<Your Aliyun OSS AccessKeySecret>',\n            'bucket'        => '<OSS bucket name>',\n            'endpoint'      => '<the endpoint of OSS, E.g: oss-cn-hangzhou.aliyuncs.com | custom domain, E.g:img.abc.com>', // OSS 外网节点或自定义外部域名\n            //'endpoint_internal' => '<internal endpoint [OSS内网节点] 如：oss-cn-shenzhen-internal.aliyuncs.com>', // v2.0.4 新增配置属性，如果为空，则默认使用 endpoint 配置(由于内网上传有点小问题未解决，请大家暂时不要使用内网节点上传，正在与阿里技术沟通中)\n            'cdnDomain'     => '<CDN domain, cdn域名>', // 如果isCName为true, getUrl会判断cdnDomain是否设定来决定返回的url，如果cdnDomain未设置，则使用endpoint来生成url，否则使用cdn\n            'ssl'           => <true|false> // true to use 'https://' and false to use 'http://'. default is false,\n            'isCName'       => <true|false> // 是否使用自定义域名,true: 则Storage.url()会使用自定义的cdn或域名生成文件url， false: 则使用外部节点生成url\n            'debug'         => <true|false>\n    ],\n    ...\n]\n```\nThen set the default driver in app/filesystems.php:\n```php\n'default' => 'oss',\n```\nOk, well! You are finish to configure. Just feel free to use Aliyun OSS like Storage!\n\n## Usage\nSee [Larave doc for Storage](https://laravel.com/docs/5.2/filesystem#custom-filesystems)\nOr you can learn here:\n\n> First you must use Storage facade\n\n```php\nuse Illuminate\\Support\\Facades\\Storage;\n```    \n> Then You can use all APIs of laravel Storage\n\n```php\nStorage::disk('oss'); // if default filesystems driver is oss, you can skip this step\n\n//fetch all files of specified bucket(see upond configuration)\nStorage::files($directory);\nStorage::allFiles($directory);\n\nStorage::put('path/to/file/file.jpg', $contents); //first parameter is the target file path, second paramter is file content\nStorage::putFile('path/to/file/file.jpg', 'local/path/to/local_file.jpg'); // upload file from local path\n\nStorage::get('path/to/file/file.jpg'); // get the file object by path\nStorage::exists('path/to/file/file.jpg'); // determine if a given file exists on the storage(OSS)\nStorage::size('path/to/file/file.jpg'); // get the file size (Byte)\nStorage::lastModified('path/to/file/file.jpg'); // get date of last modification\n\nStorage::directories($directory); // Get all of the directories within a given directory\nStorage::allDirectories($directory); // Get all (recursive) of the directories within a given directory\n\nStorage::copy('old/file1.jpg', 'new/file1.jpg');\nStorage::move('old/file1.jpg', 'new/file1.jpg');\nStorage::rename('path/to/file1.jpg', 'path/to/file2.jpg');\n\nStorage::prepend('file.log', 'Prepended Text'); // Prepend to a file.\nStorage::append('file.log', 'Appended Text'); // Append to a file.\n\nStorage::delete('file.jpg');\nStorage::delete(['file1.jpg', 'file2.jpg']);\n\nStorage::makeDirectory($directory); // Create a directory.\nStorage::deleteDirectory($directory); // Recursively delete a directory.It will delete all files within a given directory, SO Use with caution please.\n\n// upgrade logs\n// new plugin for v2.0 version\nStorage::putRemoteFile('target/path/to/file/jacob.jpg', 'http://example.com/jacob.jpg'); //upload remote file to storage by remote url\n// new function for v2.0.1 version\nStorage::url('path/to/img.jpg') // get the file url\n```\n\n## Documentation\nMore development detail see [Aliyun OSS DOC](https://help.aliyun.com/document_detail/32099.html?spm=5176.doc31981.6.335.eqQ9dM)\n## License\nSource code is release under MIT license. Read LICENSE file for more information.\n"
  },
  {
    "path": "src/AliOssAdapter.php",
    "content": "<?php\n/**\n * Created by jacob.\n * Date: 2016/5/19 0019\n * Time: 下午 17:07\n */\n\nnamespace Jacobcyl\\AliOSS;\n\nuse Dingo\\Api\\Contract\\Transformer\\Adapter;\nuse League\\Flysystem\\Adapter\\AbstractAdapter;\nuse League\\Flysystem\\AdapterInterface;\nuse League\\Flysystem\\Config;\nuse League\\Flysystem\\Util;\nuse OSS\\Core\\OssException;\nuse OSS\\OssClient;\nuse Log;\nuse Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException;\n\nclass AliOssAdapter extends AbstractAdapter\n{\n    /**\n     * @var Log debug Mode true|false\n     */\n    protected $debug;\n    /**\n     * @var array\n     */\n    protected static $resultMap = [\n        'Body'           => 'raw_contents',\n        'Content-Length' => 'size',\n        'ContentType'    => 'mimetype',\n        'Size'           => 'size',\n        'StorageClass'   => 'storage_class',\n    ];\n\n    /**\n     * @var array\n     */\n    protected static $metaOptions = [\n        'CacheControl',\n        'Expires',\n        'ServerSideEncryption',\n        'Metadata',\n        'ACL',\n        'ContentType',\n        'ContentDisposition',\n        'ContentLanguage',\n        'ContentEncoding',\n    ];\n\n    protected static $metaMap = [\n        'CacheControl'         => 'Cache-Control',\n        'Expires'              => 'Expires',\n        'ServerSideEncryption' => 'x-oss-server-side-encryption',\n        'Metadata'             => 'x-oss-metadata-directive',\n        'ACL'                  => 'x-oss-object-acl',\n        'ContentType'          => 'Content-Type',\n        'ContentDisposition'   => 'Content-Disposition',\n        'ContentLanguage'      => 'response-content-language',\n        'ContentEncoding'      => 'Content-Encoding',\n    ];\n\n    //Aliyun OSS Client OssClient\n    protected $client;\n    //bucket name\n    protected $bucket;\n\n    protected $endPoint;\n    \n    protected $cdnDomain;\n\n    protected $ssl;\n\n    protected $isCname;\n\n    //配置\n    protected $options = [\n        'Multipart'   => 128\n    ];\n\n\n    /**\n     * AliOssAdapter constructor.\n     *\n     * @param OssClient $client\n     * @param string    $bucket\n     * @param string    $endPoint\n     * @param bool      $ssl\n     * @param bool      $isCname\n     * @param bool      $debug\n     * @param null      $prefix\n     * @param array     $options\n     */\n    public function __construct(\n        OssClient $client,\n        $bucket,\n        $endPoint,\n        $ssl,\n        $isCname = false,\n        $debug = false,\n        $cdnDomain,\n        $prefix = null,\n        array $options = []\n    )\n    {\n        $this->debug = $debug;\n        $this->client = $client;\n        $this->bucket = $bucket;\n        $this->setPathPrefix($prefix);\n        $this->endPoint = $endPoint;\n        $this->ssl = $ssl;\n        $this->isCname = $isCname;\n        $this->cdnDomain = $cdnDomain;\n        $this->options = array_merge($this->options, $options);\n    }\n\n    /**\n     * Get the OssClient bucket.\n     *\n     * @return string\n     */\n    public function getBucket()\n    {\n        return $this->bucket;\n    }\n\n    /**\n     * Get the OSSClient instance.\n     *\n     * @return OssClient\n     */\n    public function getClient()\n    {\n        return $this->client;\n    }\n\n    /**\n     * Write a new file.\n     *\n     * @param string $path\n     * @param string $contents\n     * @param Config $config Config object\n     *\n     * @return array|false false on failure file meta data on success\n     */\n    public function write($path, $contents, Config $config)\n    {\n        $object = $this->applyPathPrefix($path);\n        $options = $this->getOptions($this->options, $config);\n\n        if (! isset($options[OssClient::OSS_LENGTH])) {\n            $options[OssClient::OSS_LENGTH] = Util::contentSize($contents);\n        }\n        if (! isset($options[OssClient::OSS_CONTENT_TYPE])) {\n            $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, $contents);\n        }\n        try {\n            $this->client->putObject($this->bucket, $object, $contents, $options);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n        return $this->normalizeResponse($options, $path);\n    }\n\n    /**\n     * Write a new file using a stream.\n     *\n     * @param string $path\n     * @param resource $resource\n     * @param Config $config Config object\n     *\n     * @return array|false false on failure file meta data on success\n     */\n    public function writeStream($path, $resource, Config $config)\n    {\n        $options = $this->getOptions($this->options, $config);\n        $contents = stream_get_contents($resource);\n\n        return $this->write($path, $contents, $config);\n    }\n\n    public function writeFile($path, $filePath, Config $config){\n        $object = $this->applyPathPrefix($path);\n        $options = $this->getOptions($this->options, $config);\n\n        $options[OssClient::OSS_CHECK_MD5] = true;\n\n        if (! isset($options[OssClient::OSS_CONTENT_TYPE])) {\n            $options[OssClient::OSS_CONTENT_TYPE] = Util::guessMimeType($path, '');\n        }\n        try {\n            $this->client->uploadFile($this->bucket, $object, $filePath, $options);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n        return $this->normalizeResponse($options, $path);\n    }\n\n    /**\n     * Update a file.\n     *\n     * @param string $path\n     * @param string $contents\n     * @param Config $config Config object\n     *\n     * @return array|false false on failure file meta data on success\n     */\n    public function update($path, $contents, Config $config)\n    {\n        if (! $config->has('visibility') && ! $config->has('ACL')) {\n            $config->set(static::$metaMap['ACL'], $this->getObjectACL($path));\n        }\n        // $this->delete($path);\n        return $this->write($path, $contents, $config);\n    }\n\n    /**\n     * Update a file using a stream.\n     *\n     * @param string $path\n     * @param resource $resource\n     * @param Config $config Config object\n     *\n     * @return array|false false on failure file meta data on success\n     */\n    public function updateStream($path, $resource, Config $config)\n    {\n        $contents = stream_get_contents($resource);\n        return $this->update($path, $contents, $config);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function rename($path, $newpath)\n    {\n        if (! $this->copy($path, $newpath)){\n            return false;\n        }\n\n        return $this->delete($path);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function copy($path, $newpath)\n    {\n        $object = $this->applyPathPrefix($path);\n        $newObject = $this->applyPathPrefix($newpath);\n        try{\n            $this->client->copyObject($this->bucket, $object, $this->bucket, $newObject);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function delete($path)\n    {\n        $bucket = $this->bucket;\n        $object = $this->applyPathPrefix($path);\n\n        try{\n            $this->client->deleteObject($bucket, $object);\n        }catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n\n        return ! $this->has($path);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function deleteDir($dirname)\n    {\n        $dirname = rtrim($this->applyPathPrefix($dirname), '/').'/';\n        $dirObjects = $this->listDirObjects($dirname, true);\n\n        if(count($dirObjects['objects']) > 0 ){\n\n            foreach($dirObjects['objects'] as $object)\n            {\n                $objects[] = $object['Key'];\n            }\n\n            try {\n                $this->client->deleteObjects($this->bucket, $objects);\n            } catch (OssException $e) {\n                $this->logErr(__FUNCTION__, $e);\n                return false;\n            }\n\n        }\n\n        try {\n            $this->client->deleteObject($this->bucket, $dirname);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n\n        return true;\n    }\n\n    /**\n     * 列举文件夹内文件列表；可递归获取子文件夹；\n     * @param string $dirname 目录\n     * @param bool $recursive 是否递归\n     * @return mixed\n     * @throws OssException\n     */\n    public function listDirObjects($dirname = '', $recursive =  false)\n    {\n        $delimiter = '/';\n        $nextMarker = '';\n        $maxkeys = 1000;\n\n        //存储结果\n        $result = [];\n\n        while(true){\n            $options = [\n                'delimiter' => $delimiter,\n                'prefix'    => $dirname,\n                'max-keys'  => $maxkeys,\n                'marker'    => $nextMarker,\n            ];\n\n            try {\n                $listObjectInfo = $this->client->listObjects($this->bucket, $options);\n            } catch (OssException $e) {\n                $this->logErr(__FUNCTION__, $e);\n                // return false;\n                throw $e;\n            }\n\n            $nextMarker = $listObjectInfo->getNextMarker(); // 得到nextMarker，从上一次listObjects读到的最后一个文件的下一个文件开始继续获取文件列表\n            $objectList = $listObjectInfo->getObjectList(); // 文件列表\n            $prefixList = $listObjectInfo->getPrefixList(); // 目录列表\n\n            if (!empty($objectList)) {\n                foreach ($objectList as $objectInfo) {\n\n                    $object['Prefix']       = $dirname;\n                    $object['Key']          = $objectInfo->getKey();\n                    $object['LastModified'] = $objectInfo->getLastModified();\n                    $object['eTag']         = $objectInfo->getETag();\n                    $object['Type']         = $objectInfo->getType();\n                    $object['Size']         = $objectInfo->getSize();\n                    $object['StorageClass'] = $objectInfo->getStorageClass();\n\n                    $result['objects'][] = $object;\n                }\n            }else{\n                $result[\"objects\"] = [];\n            }\n\n            if (!empty($prefixList)) {\n                foreach ($prefixList as $prefixInfo) {\n                    $result['prefix'][] = $prefixInfo->getPrefix();\n                }\n            }else{\n                $result['prefix'] = [];\n            }\n\n            //递归查询子目录所有文件\n            if($recursive){\n                foreach( $result['prefix'] as $pfix){\n                    $next  =  $this->listDirObjects($pfix , $recursive);\n                    $result[\"objects\"] = array_merge($result['objects'], $next[\"objects\"]);\n                }\n            }\n\n            //没有更多结果了\n            if ($nextMarker === '') {\n                break;\n            }\n        }\n\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function createDir($dirname, Config $config)\n    {\n        $object = $this->applyPathPrefix($dirname);\n        $options = $this->getOptionsFromConfig($config);\n\n        try {\n            $this->client->createObjectDir($this->bucket, $object, $options);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n\n        return ['path' => $dirname, 'type' => 'dir'];\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function setVisibility($path, $visibility)\n    {\n        $object = $this->applyPathPrefix($path);\n        $acl = ( $visibility === AdapterInterface::VISIBILITY_PUBLIC ) ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;\n\n        $this->client->putObjectAcl($this->bucket, $object, $acl);\n\n        return compact('visibility');\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function has($path)\n    {\n        $object = $this->applyPathPrefix($path);\n\n        return $this->client->doesObjectExist($this->bucket, $object);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function read($path)\n    {\n        $result = $this->readObject($path);\n        $result['contents'] = (string) $result['raw_contents'];\n        unset($result['raw_contents']);\n        return $result;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function readStream($path)\n    {\n        $result = $this->readObject($path);\n        $result['stream'] = $result['raw_contents'];\n        rewind($result['stream']);\n        // Ensure the EntityBody object destruction doesn't close the stream\n        $result['raw_contents']->detachStream();\n        unset($result['raw_contents']);\n\n        return $result;\n    }\n\n    /**\n     * Read an object from the OssClient.\n     *\n     * @param string $path\n     *\n     * @return array\n     */\n    protected function readObject($path)\n    {\n        $object = $this->applyPathPrefix($path);\n\n        $result['Body'] = $this->client->getObject($this->bucket, $object);\n        $result = array_merge($result, ['type' => 'file']);\n        return $this->normalizeResponse($result, $path);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function listContents($directory = '', $recursive = false)\n    {\n        $dirObjects = $this->listDirObjects($directory, true);\n        $contents = $dirObjects[\"objects\"];\n\n        $result = array_map([$this, 'normalizeResponse'], $contents);\n        $result = array_filter($result, function ($value) {\n            return $value['path'] !== false;\n        });\n\n        return Util::emulateDirectories($result);\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getMetadata($path)\n    {\n        $object = $this->applyPathPrefix($path);\n\n        try {\n            $objectMeta = $this->client->getObjectMeta($this->bucket, $object);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n\n        return $objectMeta;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getSize($path)\n    {\n        $object = $this->getMetadata($path);\n        $object['size'] = $object['content-length'];\n        return $object;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getMimetype($path)\n    {\n        if( $object = $this->getMetadata($path))\n            $object['mimetype'] = $object['content-type'];\n        return $object;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getTimestamp($path)\n    {\n        if( $object = $this->getMetadata($path))\n            $object['timestamp'] = strtotime( $object['last-modified'] );\n        return $object;\n    }\n\n    /**\n     * {@inheritdoc}\n     */\n    public function getVisibility($path)\n    {\n        $object = $this->applyPathPrefix($path);\n        try {\n            $acl = $this->client->getObjectAcl($this->bucket, $object);\n        } catch (OssException $e) {\n            $this->logErr(__FUNCTION__, $e);\n            return false;\n        }\n        \n        if ($acl == OssClient::OSS_ACL_TYPE_PUBLIC_READ ){\n            $res['visibility'] = AdapterInterface::VISIBILITY_PUBLIC;\n        }else{\n            $res['visibility'] = AdapterInterface::VISIBILITY_PRIVATE;\n        }\n\n        return $res;\n    }\n\n\n    /**\n     * @param $path\n     *\n     * @return string\n     */\n    public function getUrl( $path )\n    {\n        if (!$this->has($path)) throw new FileNotFoundException($filePath.' not found');\n        return ( $this->ssl ? 'https://' : 'http://' ) . ( $this->isCname ? ( $this->cdnDomain == '' ? $this->endPoint : $this->cdnDomain ) : $this->bucket . '.' . $this->endPoint ) . '/' . ltrim($path, '/');\n    }\n\n    /**\n     * The the ACL visibility.\n     *\n     * @param string $path\n     *\n     * @return string\n     */\n    protected function getObjectACL($path)\n    {\n        $metadata = $this->getVisibility($path);\n\n        return $metadata['visibility'] === AdapterInterface::VISIBILITY_PUBLIC ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;\n    }\n\n    /**\n     * Normalize a result from OSS.\n     *\n     * @param array  $object\n     * @param string $path\n     *\n     * @return array file metadata\n     */\n    protected function normalizeResponse(array $object, $path = null)\n    {\n        $result = ['path' => $path ?: $this->removePathPrefix(isset($object['Key']) ? $object['Key'] : $object['Prefix'])];\n        $result['dirname'] = Util::dirname($result['path']);\n\n        if (isset($object['LastModified'])) {\n            $result['timestamp'] = strtotime($object['LastModified']);\n        }\n\n        if (substr($result['path'], -1) === '/') {\n            $result['type'] = 'dir';\n            $result['path'] = rtrim($result['path'], '/');\n\n            return $result;\n        }\n        \n        $result = array_merge($result, Util::map($object, static::$resultMap), ['type' => 'file']);\n\n        return $result;\n    }\n\n    /**\n     * Get options for a OSS call. done\n     *\n     * @param array  $options\n     *\n     * @return array OSS options\n     */\n    protected function getOptions(array $options = [], Config $config = null)\n    {\n        $options = array_merge($this->options, $options);\n\n        if ($config) {\n            $options = array_merge($options, $this->getOptionsFromConfig($config));\n        }\n\n        return array(OssClient::OSS_HEADERS => $options);\n    }\n\n    /**\n     * Retrieve options from a Config instance. done\n     *\n     * @param Config $config\n     *\n     * @return array\n     */\n    protected function getOptionsFromConfig(Config $config)\n    {\n        $options = [];\n\n        foreach (static::$metaOptions as $option) {\n            if (! $config->has($option)) {\n                continue;\n            }\n            $options[static::$metaMap[$option]] = $config->get($option);\n        }\n\n        if ($visibility = $config->get('visibility')) {\n            // For local reference\n            // $options['visibility'] = $visibility;\n            // For external reference\n            $options['x-oss-object-acl'] = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? OssClient::OSS_ACL_TYPE_PUBLIC_READ : OssClient::OSS_ACL_TYPE_PRIVATE;\n        }\n\n        if ($mimetype = $config->get('mimetype')) {\n            // For local reference\n            // $options['mimetype'] = $mimetype;\n            // For external reference\n            $options['Content-Type'] = $mimetype;\n        }\n\n        return $options;\n    }\n\n    /**\n     * @param $fun string function name : __FUNCTION__\n     * @param $e\n     */\n    protected function logErr($fun, $e){\n        if( $this->debug ){\n            Log::error($fun . \": FAILED\");\n            Log::error($e->getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "src/AliOssServiceProvider.php",
    "content": "<?php\n\nnamespace Jacobcyl\\AliOSS;\n\nuse Jacobcyl\\AliOSS\\Plugins\\PutFile;\nuse Jacobcyl\\AliOSS\\Plugins\\PutRemoteFile;\nuse Illuminate\\Support\\Facades\\Storage;\nuse Illuminate\\Support\\Facades\\Log;\nuse Illuminate\\Support\\ServiceProvider;\nuse League\\Flysystem\\Filesystem;\nuse OSS\\OssClient;\n\nclass AliOssServiceProvider extends ServiceProvider\n{\n\n    /**\n     * Bootstrap the application services.\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        //发布配置文件\n        /*\n        if (function_exists('config_path')) {\n            $this->publishes([\n                __DIR__ . '/config/config.php' => config_path('alioss.php'),\n            ], 'config');\n        }\n        */\n\n        Storage::extend('oss', function($app, $config)\n        {\n            $accessId  = $config['access_id'];\n            $accessKey = $config['access_key'];\n\n            $cdnDomain = empty($config['cdnDomain']) ? '' : $config['cdnDomain'];\n            $bucket    = $config['bucket'];\n            $ssl       = empty($config['ssl']) ? false : $config['ssl']; \n            $isCname   = empty($config['isCName']) ? false : $config['isCName'];\n            $debug     = empty($config['debug']) ? false : $config['debug'];\n\n            $endPoint  = $config['endpoint']; // 默认作为外部节点\n            $epInternal= $isCname?$cdnDomain:(empty($config['endpoint_internal']) ? $endPoint : $config['endpoint_internal']); // 内部节点\n            \n            if($debug) Log::debug('OSS config:', $config);\n\n            $client  = new OssClient($accessId, $accessKey, $epInternal, $isCname);\n            $adapter = new AliOssAdapter($client, $bucket, $endPoint, $ssl, $isCname, $debug, $cdnDomain);\n\n            //Log::debug($client);\n            $filesystem =  new Filesystem($adapter);\n            \n            $filesystem->addPlugin(new PutFile());\n            $filesystem->addPlugin(new PutRemoteFile());\n            //$filesystem->addPlugin(new CallBack());\n            return $filesystem;\n        });\n    }\n\n    /**\n     * Register the application services.\n     *\n     * @return void\n     */\n    public function register()\n    {\n    }\n\n}\n"
  },
  {
    "path": "src/Plugins/PutFile.php",
    "content": "<?php\n/**\n * Created by jacob.\n * User: jacob\n * Date: 16/5/20\n * Time: 下午8:31\n */\n\nnamespace Jacobcyl\\AliOSS\\Plugins;\n\nuse Illuminate\\Support\\Facades\\Log;\nuse League\\Flysystem\\Config;\nuse League\\Flysystem\\Plugin\\AbstractPlugin;\n\nclass PutFile extends AbstractPlugin\n{\n\n    /**\n     * Get the method name.\n     *\n     * @return string\n     */\n    public function getMethod()\n    {\n        return 'putFile';\n    }\n\n    public function handle($path, $filePath, array $options = []){\n        $config = new Config($options);\n        if (method_exists($this->filesystem, 'getConfig')) {\n            $config->setFallback($this->filesystem->getConfig());\n        }\n        \n        return (bool)$this->filesystem->getAdapter()->writeFile($path, $filePath, $config);\n    }\n}"
  },
  {
    "path": "src/Plugins/PutRemoteFile.php",
    "content": "<?php\n/**\n * Created by jacob.\n * User: jacob\n * Date: 16/5/20\n * Time: 下午8:31\n */\nnamespace Jacobcyl\\AliOSS\\Plugins;\nuse League\\Flysystem\\Config;\nuse League\\Flysystem\\Plugin\\AbstractPlugin;\nclass PutRemoteFile extends AbstractPlugin\n{\n    /**\n     * Get the method name.\n     *\n     * @return string\n     */\n    public function getMethod()\n    {\n        return 'putRemoteFile';\n    }\n\n    public function handle($path, $remoteUrl, array $options = []){\n        $config = new Config($options);\n        if (method_exists($this->filesystem, 'getConfig')) {\n            $config->setFallback($this->filesystem->getConfig());\n        }\n\n        //Get file stream from remote url\n        $resource = fopen($remoteUrl, 'r');\n\n        return (bool)$this->filesystem->getAdapter()->writeStream($path, $resource, $config);\n    }\n}\n"
  },
  {
    "path": "src/config/config.php",
    "content": "<?php\nreturn [\n    'debug' => true,\n];"
  }
]