laravel 添加 七牛云 存储驱动

接着上2篇笔记:
	使用 qshell 将本地文件同步到七牛云
		https://blog.csdn.net/beyond__devil/article/details/83030702

	将 laravel 项目内静态文件,css、js、images 部署到七牛云 csdn
		https://blog.csdn.net/beyond__devil/article/details/83057313

本篇笔记,我们根据最后遗留的问题,扩展 laravel 文件系统驱动,添加 七牛云 存储引擎。

参考文章:
	laravel 文件存储文档
		https://laravelacademy.org/post/9567.html
	基于七牛云 PHP SDK + Laravel 文件存储实现 Laravel 学院静态资源云存储及 CDN 加速
		https://laravelacademy.org/post/9486.html

昨晚因为一个小问题,让我调试代码,调试了N久,看了好久的 filesystem 源码,稍微总结下:
	文件系统的 Facade,我们使用的是:
		Storage

	Storage Facade 的定义,我们在门面类列表查看:
		https://laravelacademy.org/post/9536.html

		filesystem - Illuminate\Filesystem\FilesystemManager
		filesystem.disk - 	Illuminate\Contracts\Filesystem\Filesystem

	打开 Illuminate\Filesystem 目录,简单看下:
		Cache.php - 缓存有关
		Filesystem.php - 文件、目录处理类
		FilesystemAdapter.php - 设配器(laravel)
		FilesystemManager.php - 存储驱动管理
		FilesystemServiceProvider.php - 服务提供者

	laravel 文件系统,底层使用的 league/flysystem 包,这里我们有时间也可以简单看下源码,大概知道是个什么意思就行。

	注意:
		我们只需要知道我们平常使用:
			Storage::xxx() 调用文件存储方法
		其实调用的就是: Illuminate\Filesystem\FilesystemManager 就可以。然后我们一点点看源码,基本就都能明白了(Laraval 有时候很复杂,半天找不到对应的到底是哪个文件)

		Storage::extend() 就是 Illuminate\Filesystem\FilesystemManager->extend(),用于扩展我们自定义的 '文件存储'

	Storage::xxx() 方法有:
		FilesystemManager.php:	
			drive() - 同 disk()
			disk() - 获取文件系统实例,如果没有,使用默认驱动(config/filesystem.php 有2个默认配置,一个默认配置,一个默认云存储配置)
			cloud() - 获取默认云存储(config/filesystem.php 有2个默认配置,一个默认配置,一个默认云存储配置)
			createLocalDriver() - 创建一个 local 驱动
			createFtpDriver() - 创建一个 ftp 驱动
			createSftpDriver() - 创建一个 sftp 驱动
			createS3Driver() - 创建一个 Amazon S3 驱动
			createRackspaceDriver() - 创建一个 Rackspace 驱动
			set() - 获取给定的驱动实例
			getDefaultDriver() - 获取默认驱动名
			getDefaultCloudDriver() - 获取默认云存储驱动名
			forgetDisk() - unset 给定的驱动
			extend() - 注册一个自定义驱动
			__call($method, $parameters)		// Storage::xxx() 其他方法,都是通过魔术方法 __call() 来调用 '默认驱动' 的方法,$this->dist() 返回的是 'FilesystemAdapter.php' 类的实例
			{
				return $this->disk()->$method(...$parameters);
			}

		FilesystemAdapter.php:
			里面经常会调用 $this->driver() 方法,而 $this->driver() 都是 'league/flysystem/Filesystem.php' 的实例。它里面对应的所有方法,都是对参数进行处理,最终调用的就是我们具体的 '驱动适配器'(local、public、s3、qiniu) 的方法了,所以我们就暂且忽略 'league/flysystem/Filesystem.php' 这个中间处理层了
			assertExists() - PHPUnit 测试使用
			assertMissing() - PHPUnit 测试使用
			exists($path) - 文件是否存在,调用 adaptar 的 has()
			path($path) - 获取给定文件的全路径,调用 adaptar 的 getPathPrefix() . $path
			get($path) - 获取文件内容,调用 adaptar 的 read()
			response($path, $name = null, array $headers = [], $disposition = 'inline') - 创建给定文件的流响应(stream),中间会调用 '自身的' readStream() 方法
			download() - 借助 response() 方法,下载
			put($path, $contents, $options = []) - 将制定内容写入到文件中,根据不同情况,调用不同的写入方法。有 '自身的 putFile()','adaptar 的 putStream()','adaptar 的 put()'
			putFile($path, $file, $options = []) - 将上传文件,写入到存储,调用 '自身的 putFileAs()'
			putFileAs($path, $file, $name, $options = []) - 将上传文件,写入到存储,调用上面写的 '自身的 put()'
			getVisibility($path) - 获取文件的可见性,调用 'adaptar 的 getVisibility()'
			setVisibility($path) - 设置文件的可见性,调用 'adaptar 的 setVisibility()'
			prepend($path, $data, $separator = PHP_EOL) - 向前追加指定内容到文件,调用 '自身的 put()'(文件存在,追加,不存在,新建)
			append($path, $data, $separator = PHP_EOL) - 向后追加指定内容到文件,调用 '自身的 put()'(文件存在,追加,不存在,新建)
			delete($paths) - 删除指定文件(支持多个,数组,或者多个参数,会使用 file_get_args() 来获取),调用 'adaptar 的 delete()'
			copy($from, $to) - 复制文件,调用 'adaptar 的 coty()'
			move($from, $to) - 移动文件,调用 'adaptar 的 rename()'
			size($path) - 获取文件大小,调用 'adaptar 的 getSize()'
			mimeType($path) - 获取文件的 MIME 类型,调用 'adaptar 的 getMimeType()'
			lastModified($path) - 获取文件的最后修改时间,调用 'adaptar 的 getTimestamp()'
			url($path) - 获取文件的访问 URL,『我们自己需要定义一个 getUrl() 方法,这个方法很关键,我们可以在我们的 qiniuAdaptar 里添加 getUrl() 方法,然后静态文件的访问,我们可以直接使用 Storage::geturl() 来获取』
			readStream($path)
			writeStream($path, $resource, array $options = [])
			temporaryUrl($path, $expiration, array $options = [])
			getAwsTemporaryUrl($adapter, $path, $expiration, $options)
			getRackspaceTemporaryUrl
			files($directory = null, $recursive = false) - 获取目录下的所有文件,类型只是 'file',调用 'adaptar 的 listContents()'
			allFiles($directory = null) - 获取目录下的所有文件(递归),调用上面的 $this->files()
			Directories($directory = null, $recursive = false) - 获取目录下的所有目录,类型只是 'dir',调用 'adaptar 的 listContents()'
			allDirectories($directory = null) - 获取目录下的所有目录(递归),调用上面的 $this->Directories()
			makeDirectory($path) - 创建目录,调用 'adaptar 的 createDir()'
			deleteDirectory($path) - 删除目录,调用 'adaptar 的 deleteDir()'
			flushCache() - 只有当该 adaptar 继承了 'CacheAdapter',才可用
			getDriver() - 获取当前的 Filesystem 实例
			__call($method, array $parameters)		// Storage::xxx() 其他方法,还可以通过魔术方法 __call() 来调用 'Filesystem' 的方法(方法很少了...)
			{
				return call_user_func_array([$this->driver, $method], $parameters);
			}

		Filesystem.php 额外的方法(好多类似别名访问):
			PluggableTrait 方法,插件相关(未用到,我们不考虑)
			ConfigAwareTrait 方法,配置相关
			getAdapter() - 获取 adaptar,local、public、s3、qiniu 等
			write() - 调用 'adaptar 的 write()'
			putStream()
			readAndDelete() - 读取文件内容,并删除文件,文件不存在,返回 false,文件存储,返回文件内容
			update() - 调用 'adaptar 的 update()'
			updateStream() - 调用 'adaptar 的 updateStream() '
			read()
			readStream()
			rename()
			deleteDir()
			createDir()
			listContents()
			getMimetype()
			getTimestamp()
			getSize()
			getMetadata()
			assertPresent() - 文件不存在报错(disable_asserts 配置)
			assertAbsent() - 文件存在报错(disable_asserts 配置)

	自己语言表达能力有点差,可能自己也不是很明白,要讲清楚很难,源码总结就说到这里。

开始扩展七牛云存储,主要参考上面的 '基于七牛云 PHP SDK + Laravel 文件存储实现 Laravel 学院静态资源云存储及 CDN 加速' 这篇文章,七牛云的源码封装,我会走一遍,稍微添加或修改,最后整理处理。

	安装 '七牛云 PHP SDK':
		composer require qiniu/php-sdk

	.env 添加配置:
		FILESYSTEM_CLOUD=qiniu
		QINIU_ACCESS_KEY=你的七牛云AccessKey
		QINIU_SECRET_KEY=你的七牛云SecretKey
		QINIU_DEFAULT_REGION=你的默认区域,比如z0
		QINIU_BUCKET=你的bucket(通过在对象存储中新增存储空间获取)
		QINIU_PREFIX=七牛云空间内,文件存储的相对路径(路径前缀)
		QINIU_URL=你的静态 CDN URL

	config/filesystem.php,添加 'qiniu' 驱动:
		'disks' => [
			...
		    'qiniu' => [
		        'driver' => 'qiniu',
	            'key' => env('QINIU_ACCESS_KEY', 'xxx'),
	            'secret' => env('QINIU_SECRET_KEY', 'xxx'),
	            'region' => env('QINIU_DEFAULT_REGION', ''),
	            'bucket' => env('QINIU_BUCKET', 'xxx'),
	            'prefix' => env('QINIU_PREFIX', 'storage'),
	            'url' => env('QINIU_URL', 'xxx'),
		    ],
		],

	编写 '七牛云存储适配器':
		App/Services/FileSystem/QiniuAdapter.php
		注:
			laravel 从 5.1 之后好像取消了 App/Services 目录,我们自己来创建这个目录,再多添加一层 FileSystem,可能以后会添加其他的文件存储。(也可以指定其他目录)

	注册 '七牛云存储适配器':
		vim app/Providers/AppServiceProvider.php
		boot() 方法添加:
			use Storage;
			use League\Flysystem\Filesystem;
			use App\Services\FileSystem\QiniuAdapter;

			public function boot()
			{
				Storage::extend('qiniu', function ($app, $config) {
				    return new Filesystem(new QiniuAdapter($config));
				})
			}	

	App/Services/FileSystem/QiniuAdapter.php 适配器代码:
		QiniuAdapter 必须实现 League\Flysystem\AdapterInterface 接口
		我们可查看官方的 Local 文件存储,仿照写:
			league/flysystem/src/Adapter/Local.php
		下面是 'laravel 学院' 的代码小修改:

		/*********************************************
			<?php

			namespace App\Services\FileSystem;

			use Illuminate\Contracts\Filesystem\FileNotFoundException;
			use League\Flysystem\Adapter\AbstractAdapter;
			use League\Flysystem\Config;
			use Qiniu\Auth;
			use Qiniu\Storage\BucketManager;
			use Qiniu\Storage\UploadManager;
			use Symfony\Component\HttpFoundation\File\Exception\UploadException;

			class QiniuAdapter extends AbstractAdapter
			{

			    protected $uploadManager;
			    protected $bucketManager;
			    private $accessKey;
			    private $accessSecret;
			    private $bucketName;
			    private $token;
			    private $url;

			    public function __construct($config)
			    {
			        $this->uploadManager = new UploadManager();
			        $this->accessKey = $config['key'];
			        $this->accessSecret = $config['secret'];
			        $this->bucketName = $config['bucket'];
			        $auth = new Auth($this->accessKey, $this->accessSecret);
			        $this->bucketManager = new BucketManager($auth);
			        $this->token = $auth->uploadToken($this->bucketName);
			        $this->setPathPrefix($config['prefix']);
			        $this->url = $config['url'];
			    }

			    /**
			     * 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)
			    {
			        return $this->upload($path, $contents);
			    }

			    /**
			     * 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)
			    {
			        return $this->upload($path, $resource, true);
			    }

			    /**
			     * 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)
			    {
			        return $this->upload($path, $contents);
			    }

			    /**
			     * 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)
			    {
			        return $this->upload($path, $resource, true);
			    }

			    /**
			     * Rename a file.
			     *
			     * @param string $path
			     * @param string $newpath
			     *
			     * @return bool
			     */
			    public function rename($path, $newpath)
			    {
			        $path = $this->applyPathPrefix($path);
			        $newpath = $this->applyPathPrefix($newpath);
			        $error = $this->bucketManager->rename($this->bucketName, $path, $newpath);
			        return $error == null ? true : false;
			    }

			    /**
			     * Copy a file.
			     *
			     * @param string $path
			     * @param string $newpath
			     *
			     * @return bool
			     */
			    public function copy($path, $newpath)
			    {
			        $path = $this->applyPathPrefix($path);
			        $newpath = $this->applyPathPrefix($newpath);
			        $error = $this->bucketManager->copy($this->bucketName, $path, $this->bucketName, $newpath);
			        return $error == null ? true : false;
			    }

			    /**
			     * Delete a file.
			     *
			     * @param string $path
			     *
			     * @return bool
			     */
			    public function delete($path)
			    {
			        $this->applyPathPrefix($path);
			        $error = $this->bucketManager->delete($this->bucketName, $path);
			        return $error == null ? true : false;
			    }

			    /**
			     * Delete a directory.
			     *
			     * @param string $dirname
			     *
			     * @return bool
			     */
			    public function deleteDir($dirname)
			    {
			        throw new \BadFunctionCallException('暂不支持该操作');
			    }

			    /**
			     * Create a directory.
			     *
			     * @param string $dirname directory name
			     * @param Config $config
			     *
			     * @return array|false
			     */
			    public function createDir($dirname, Config $config)
			    {
			        throw new \BadFunctionCallException('暂不支持该操作');
			    }

			    /**
			     * Set the visibility for a file.
			     *
			     * @param string $path
			     * @param string $visibility
			     *
			     * @return array|false file meta data
			     */
			    public function setVisibility($path, $visibility)
			    {
			        throw new \BadFunctionCallException('暂不支持该操作');
			    }

			    /**
			     * Check whether a file exists.
			     *
			     * @param string $path
			     *
			     * @return array|bool|null
			     */
			    public function has($path)
			    {
			        $path = $this->applyPathPrefix($path);
			        $stat = $this->bucketManager->stat($this->bucketName, $path);
			        if ($stat[0] == null) {
			            return false;
			        } else {
			            return true;
			        }
			    }

			    /**
			     * Read a file.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function read($path)
			    {
			        $path = $this->applyPathPrefix($path);
			        list($fileInfo, $error) = $this->bucketManager->stat($this->bucketName, $path);
			        if ($fileInfo) {
			            return $fileInfo;
			        } else {
			            throw new FileNotFoundException('对应文件不存在');
			        }
			    }

			    /**
			     * Read a file as a stream.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function readStream($path)
			    {
			        throw new \BadFunctionCallException('暂不支持该操作');
			    }

			    /**
			     * List contents of a directory.
			     *
			     * @param string $directory
			     * @param bool $recursive
			     *
			     * @return array
			     */
			    public function listContents($directory = '', $recursive = false)
			    {
			        return $this->bucketManager->listFiles($this->bucketName);
			    }

			    /**
			     * Get all the meta data of a file or directory.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function getMetadata($path)
			    {
			        return $this->read($path);
			    }

			    /**
			     * Get the size of a file.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function getSize($path)
			    {
			        $fileInfo = $this->read($path);
			        return $fileInfo['fsize'];
			    }

			    /**
			     * Get the mimetype of a file.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function getMimetype($path)
			    {
			        $fileInfo = $this->read($path);
			        return $fileInfo['fileType'];
			    }

			    /**
			     * Get the last modified time of a file as a timestamp.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function getTimestamp($path)
			    {
			        $fileInfo = $this->read($path);
			        return $fileInfo['putTime'];
			    }

			    /**
			     * Get the visibility of a file.
			     *
			     * @param string $path
			     *
			     * @return array|false
			     */
			    public function getVisibility($path)
			    {
			        throw new \BadFunctionCallException('暂不支持该操作');
			    }

			    protected function upload(string $path, $contents, $stream = false)
			    {
			        $path = $this->applyPathPrefix($path);
			        try {
			            if ($stream) {
			                $response = $this->uploadManager->put($this->token, $path, $contents);
			            } else {
			                $response = $this->uploadManager->putFile($this->token, $path, $contents);
			            }
			        } catch (\Exception $ex) {
			            throw $ex;
			        } catch (\Exception $ex) {
			            throw $ex;
			        }
			        list($uploadResult, $error) = $response;
			        if ($uploadResult) {
			            return $uploadResult;
			        } else {
			            throw new UploadException('上传文件到七牛失败:' . $error->message());
			        }
			    }

			    public function getUrl($path)
			    {
			        $path = $this->applyPathPrefix($path);
			        return $this->url . DIRECTORY_SEPARATOR . $path;
			    }
			}

		*********************************************/

这个 QiniuAdapter 有不少问题,因为好多方法其实未实现~~,打算好好看看 七牛云 SDK & 七牛云开发者文档,看看能不能把这个 QiniuAdapter 给写的更好一点。看了会,发现有点不太好搞啊~~

查找资料过程中,在 packagist.org 输入 qiniu,本想找下 qiniu/php-sdk,发现了好多七牛相关的包,被打脸了!!!这篇笔记有点长,接下篇,终结下七牛云存储的问题!!!

	

猜你喜欢

转载自blog.csdn.net/beyond__devil/article/details/83065677
今日推荐