Laravel 5.5 集成 七牛云存储

提示: 本教程配置了 两个 七牛云空间列表额: imagescdns

背景

随着云服务的流行,将图片等静态文件存储到云服务提供商,然后通过CDN的方式获取成为了静态文件存储与加载的通用解决方案,这样做的好处是显而易见的,一方面可以免除在自己的系统上实现文件分布式存储(对大型系统而言),将专业的事情交给专业的人去做;另一方面,CDN获取静态文件可以极大节省服务器带宽,有效提高系统高峰期的吞吐能力,学院正是通过这种方式解决了网站访问速度的问题,用1M带宽撑起学院每日数万的访问量。

现在提供云存储的服务商也很多,阿里云、腾讯云这类整体云服务解决方案提供商自然提供了类似的服务,此外还有一些专业的云存储服务提供商,比如七牛云、又拍云等,由于老版学院使用的是七牛云,所以新版学院继续沿用,以减少新平台的学习成本。

云存储实现

1. 初始化配置文件

七牛云官方提供了 PHP SDK,我们基于这个 SDK 来实现上层功能,首先安装这个 SDK:

composer require qiniu/php-sdk

为了使用这个 SDK,还需要注册一个七牛云账号,然后去 个人中心->密钥管理 页面获取 AccessKey/SecretKey 信息,编辑 .env 配置文件修改相关配置如下:

# 七牛 [config/filesystems.php/disks]
QINIU_ACCESS_KEY=你的七牛云AccessKey
QINIU_SECRET_KEY=你的七牛云SecretKey
QINIU_BUCKET=你的bucket(通过在对象存储中新增存储空间获取)
QINIU_DOMAIN=你的静态 CDN URL
QINIU_BUCKET_CDNS=你的第二个bucket
QINIU_DOMAIN_CDNS=你的第二个domain

然后修改配置文件config/filesystems.php,新增七牛云存储相关配置:

'disks' => [

    ... // Laravel 自带配置信息

   'qiniu' => [
        'driver' => 'qiniu',
        'access_key' => env('QINIU_ACCESS_KEY'),
        'secret_key' => env('QINIU_SECRET_KEY'),
        'bucket' => env('QINIU_BUCKET'),
        'domain' => env('QINIU_DOMAIN'),
    ],
    'qiniu_cdns' => [
        'driver' => 'qiniu_cdns',
        'access_key' => env('QINIU_ACCESS_KEY'),
        'secret_key' => env('QINIU_SECRET_KEY'),
        'bucket' => env('QINIU_BUCKET_CDNS'),
        'domain' => env('QINIU_DOMAIN_CDNS'),
    ],
],

2. 编写七牛云存储适配器类

然后我们需要为七牛云存储编写一个 Laravel 文件存储驱动适配器:

$ touch app/Services/FileSystem/QiniuAdapter.php

<?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
{
    private $accessKey;
    private $accessSecret;

    // images.learnku.net
    protected $uploadManager;
    protected $bucketManager;
    private $bucketName;
    private $domainName;
    private $token;

    public function __construct($qiniu='qiniu', $prefix = '')
    {
        $this->accessKey = \config('filesystems.disks.'. $qiniu .'.access_key');
        $this->accessSecret = \config('filesystems.disks.'. $qiniu .'.secret_key');

        // 上传文件句柄
        $this->uploadManager = new UploadManager();
        $this->bucketName = \config('filesystems.disks.'. $qiniu .'.bucket');
        $this->domainName = \config('filesystems.disks.'. $qiniu .'.domain');
        $auth = new \Qiniu\Auth($this->accessKey, $this->accessSecret);
        // 管理文件句柄
        $this->bucketManager = new BucketManager($auth);
        $this->token = $auth->uploadToken($this->bucketName);

        $this->setPathPrefix($prefix);
    }

    /**
     * 写一个新文件。
     * 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
     * @throws \Exception
     */
    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
     * @throws \Exception
     */
    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
     * @throws \Exception
     */
    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
     * @throws \Exception
     */
    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->addPrefix($path);
        $newpath = $this->addPrefix($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->addPrefix($path);
        $newpath = $this->addPrefix($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->addPrefix($path);
        $error = $this->bucketManager->delete($this->bucketName, $path);
        return $error == null ? true : false;
    }

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

    /**
     * 创建一个目录。
     * Create a directory.
     *
     * @param string $dirname directory name
     * @param Config $config
     *
     * @return void
     */
    public function createDir($dirname, Config $config)
    {
        throw new \BadFunctionCallException('暂不支持该操作');
    }

    /**
     * 设置文件的可见性。
     * Set the visibility for a file.
     *
     * @param string $path
     * @param string $visibility
     *
     * @return void 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->addPrefix($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
     * @throws FileNotFoundException
     */
    public function read($path)
    {
        $path = $this->addPrefix($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 void
     */
    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
     * @throws FileNotFoundException
     */
    public function getMetadata($path)
    {
        return $this->read($path);
    }

    /**
     * 获取文件的大小。
     * Get the size of a file.
     *
     * @param string $path
     *
     * @return array|false
     * @throws FileNotFoundException
     */
    public function getSize($path)
    {
        $fileInfo = $this->read($path);
        return $fileInfo['fsize'];
    }

    /**
     * 获取文件的mimetype。
     * Get the mimetype of a file.
     *
     * @param string $path
     *
     * @return array|false
     * @throws FileNotFoundException
     */
    public function getMimetype($path)
    {
        $fileInfo = $this->read($path);
        return $fileInfo['fileType'];// TODO: Implement getMimetype() method.
    }

    /**
     * 获取文件的上次修改时间作为时间戳。
     * Get the last modified time of a file as a timestamp.
     *
     * @param string $path
     *
     * @return array|false
     * @throws FileNotFoundException
     */
    public function getTimestamp($path)
    {
        $fileInfo = $this->read($path);
        return $fileInfo['putTime'];
    }

    /**
     * 获取文件的可见性。
     * Get the visibility of a file.
     *
     * @param string $path
     *
     * @return void
     */
    public function getVisibility($path)
    {
        throw new \BadFunctionCallException('暂不支持该操作');
    }

    /**
     * @param string $path
     * @param $contents
     * @param bool $stream
     * @return mixed
     * @throws \Exception
     */
    protected function upload(string $path, $contents, $stream = false)
    {
        $path = $this->addPrefix($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;
        }
        list($uploadResult, $error) = $response;
        if ($uploadResult) {
            return $uploadResult;
        } else {
            throw new UploadException('上传文件到七牛失败:' . $error->message());
        }
    }

    protected function addPrefix($path)
    {
        return $this->applyPathPrefix($path);
        // return ltrim($path, '\\/');
    }
}

3. 在 AppServiceProvider 提供的 boot 方法中注册这个适配器:

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use App\Services\FileSystem\QiniuAdapter;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Filesystem;
.
.
.
class AppServiceProvider extends ServiceProvider{
    public function boot()
    {
        /**
         * 注册新的云存储驱动 七牛
         * Storage::disk('qiniu')->write('test/logo.png', storage_path('app/public/images/logo.png'));
         * Storage::disk('qiniu_cdns')->write('test/logo.png', storage_path('app/public/images/logo.png'));
         */
        Storage::extend('qiniu', function ($app, $config) {
            return new Filesystem(new QiniuAdapter('qiniu', 'uploads'));
        });
        Storage::extend('qiniu_cdns', function ($app, $config) {
            return new Filesystem(new QiniuAdapter('qiniu_cdns', ''));
        });

        .
        .
        .
    }
}

在控制器中调用

编写好以上代码之后,就可以通过类似如下方式存储图片:

Storage::disk('qiniu')->write('test/academy/logo.png', storage_path('app/public/images/logo.png'));

Storage::disk('qiniu_cdns')->write('test/academy/logo.png', storage_path('app/public/images/logo.png'));

我们在具体实现的过程中,可以在文件存储本地之后顺便将其存储到云存储中,在获取图片的时候通过 .env 中配置的域名加上上面保存的路径就可以获取到图片了

至于七牛云的 申请 和配置, 大家自行百度吧, 能看到此文, 基本上大家应该都会掌握.

讨论数量: 1

给你留个评论

good

6个月前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!