realmatrix/flysystem-google-drive-ext

Google Drive 的 Flysystem 适配器,具有无缝虚拟路径与显示路径转换功能

2.x-dev 2024-09-04 06:45 UTC

This package is not auto-updated.

Last update: 2024-10-03 05:48:21 UTC


README

Flysystem API version Latest Version on Packagist License Build Status StyleCI Total Downloads

Google 使用每个文件夹和文件的唯一 ID。这使得与其他使用正常路径的存储服务集成变得困难。

Flysystem 适配器 通过无缝地将路径从“显示路径”转换为“虚拟路径”,反之亦然来解决该问题。

例如:虚拟路径 /Xa3X9GlR6EmbnY1RLVTk5VUtOVkk/0B3X9GlR6EmbnY1RLVTk5VUtOVkk 转换为 /My Nice Dir/myFile.ext,所有 ID 处理都隐藏起来。

安装

  • 对于 Flysystem V2/V3Laravel >= 9.x.x
composer require realmatrix/flysystem-google-drive-ext
  • 对于 Flysystem V1Laravel <= 8.x.x 使用包的 1.x.x 版本
composer require realmatrix/flysystem-google-drive-ext:"^1.0.0"

获取 Google 密钥

请按照 Google Docs 获取您的 client ID, client secret & refresh token

此外,您还可以通过 @ivanvermeyen 跟随这些易于理解的教程

用法

$client = new \Google\Client();
$client->setClientId([client_id]);
$client->setClientSecret([client_secret]);
$client->refreshToken([refresh_token]);
$client->setApplicationName('My Google Drive App');

$service = new \Google\Service\Drive($client);

// variant 1
$adapter = new \Masbug\Flysystem\GoogleDriveAdapter($service, 'My_App_Root');

// variant 2: with extra options and query parameters
$adapter2 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'useDisplayPaths' => true, /* this is the default */

        /* These are global parameters sent to server along with per API parameters. Please see https://cloud.google.com/apis/docs/system-parameters for more info. */
        'parameters' => [
            /* This example tells the remote server to perform quota checks per unique user id. Otherwise the quota would be per client IP. */
            'quotaUser' => (string)$some_unique_per_user_id
        ]
    ]
);

// variant 3: connect to team drive
$adapter3 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'teamDriveId' => '0GF9IioKDqJsRGk9PVA'
    ]
);

// variant 4: connect to a folder shared with you
$adapter4 = new \Masbug\Flysystem\GoogleDriveAdapter(
    $service,
    'My_App_Root',
    [
        'sharedFolderId' => '0GF9IioKDqJsRGk9PVA'
    ]
);

$fs = new \League\Flysystem\Filesystem($adapter, new \League\Flysystem\Config([\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]));
// List selected root folder contents
$contents = $fs->listContents('', true /* is_recursive */);

// List specific folder contents
$contents = $fs->listContents('MyFolder', true /* is_recursive */);
文件上传
// Upload a file
$local_filepath = '/home/user/downloads/file_to_upload.ext';
$remote_filepath = 'MyFolder/file.ext';

$localAdapter = new \League\Flysystem\Local\LocalFilesystemAdapter('/');
$localfs = new \League\Flysystem\Filesystem($localAdapter, [\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]);

try {
    $time = Carbon::now();
    $fs->writeStream($remote_filepath, $localfs->readStream($local_filepath), new \League\Flysystem\Config());

    $speed = !(float)$time->diffInSeconds() ? 0 :filesize($local_filepath) / (float)$time->diffInSeconds();
    echo 'Elapsed time: '.$time->diffForHumans(null, true).PHP_EOL;
    echo 'Speed: '. number_format($speed/1024,2) . ' KB/s'.PHP_EOL;
} catch(\League\Flysystem\UnableToWriteFile $e) {
    echo 'UnableToWriteFile!'.PHP_EOL.$e->getMessage();
}

// NOTE: Remote folders are automatically created.
文件下载
// Download a file
$remote_filepath = 'MyFolder/file.ext';
$local_filepath = '/home/user/downloads/file.ext';

$localAdapter = new \League\Flysystem\Local\LocalFilesystemAdapter('/');
$localfs = new \League\Flysystem\Filesystem($localAdapter, [\League\Flysystem\Config::OPTION_VISIBILITY => \League\Flysystem\Visibility::PRIVATE]);

try {
    $time = Carbon::now();
    $localfs->writeStream($local_filepath, $fs->readStream($remote_filepath), new \League\Flysystem\Config());

    $speed = !(float)$time->diffInSeconds() ? 0 :filesize($local_filepath) / (float)$time->diffInSeconds();
    echo 'Elapsed time: '.$time->diffForHumans(null, true).PHP_EOL;
    echo 'Speed: '. number_format($speed/1024,2) . ' KB/s'.PHP_EOL;
} catch(\League\Flysystem\UnableToWriteFile $e) {
    echo 'UnableToWriteFile!'.PHP_EOL.$e->getMessage();
}
如何获取 TeamDrive 列表和 IDs
$drives = $fs->getAdapter()->getService()->teamdrives->listTeamdrives()->getTeamDrives();
foreach ($drives as $drive) {
    echo 'TeamDrive: ' . $drive->name . PHP_EOL;
    echo 'ID: ' . $drive->id . PHP_EOL. PHP_EOL;
}
如何永久删除用户的所有已删除文件
$fs->getAdapter()->emptyTrash([]);

与 Laravel 框架一起使用

更新 .env 文件以包含 Google 密钥

将您创建的密钥添加到您的 .env 文件中,并将 google 设置为您的默认云存储。您可以将 .env.example 文件复制并填写空白。

FILESYSTEM_CLOUD=google
GOOGLE_DRIVE_CLIENT_ID=xxx.apps.googleusercontent.com
GOOGLE_DRIVE_CLIENT_SECRET=xxx
GOOGLE_DRIVE_REFRESH_TOKEN=xxx
GOOGLE_DRIVE_FOLDER=
#GOOGLE_DRIVE_TEAM_DRIVE_ID=xxx
#GOOGLE_DRIVE_SHARED_FOLDER_ID=xxx

# you can use more accounts, only add more configs
#SECOND_GOOGLE_DRIVE_CLIENT_ID=xxx.apps.googleusercontent.com
#SECOND_GOOGLE_DRIVE_CLIENT_SECRET=xxx
#SECOND_GOOGLE_DRIVE_REFRESH_TOKEN=xxx
#SECOND_GOOGLE_DRIVE_FOLDER=backups
#SECOND_DRIVE_TEAM_DRIVE_ID=xxx
#SECOND_DRIVE_SHARED_FOLDER_ID=xxx
config/filesystems.php 中添加磁盘
'disks' => [
    // ...
    'google' => [
        'driver' => 'google',
        'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'),
        'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
        'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'),
        'folder' => env('GOOGLE_DRIVE_FOLDER'), // without folder is root of drive or team drive
        //'teamDriveId' => env('GOOGLE_DRIVE_TEAM_DRIVE_ID'),
        //'sharedFolderId' => env('GOOGLE_DRIVE_SHARED_FOLDER_ID'),
    ],
    // you can use more accounts, only add more disks and configs on .env
    // also you can use the same account and point to a diferent folders for each disk
    /*'second_google' => [
        'driver' => 'google',
        'clientId' => env('SECOND_GOOGLE_DRIVE_CLIENT_ID'),
        'clientSecret' => env('SECOND_GOOGLE_DRIVE_CLIENT_SECRET'),
        'refreshToken' => env('SECOND_GOOGLE_DRIVE_REFRESH_TOKEN'),
        'folder' => env('SECOND_GOOGLE_DRIVE_FOLDER'),
    ],*/
    // ...
],
app/Providers/ 路径的 ServiceProvider 中添加存储驱动

示例

namespace App\Providers;

use Illuminate\Support\Facades\Storage;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider { // can be a custom ServiceProvider
    // ...
    public function boot(){
        // ...
        try {
            \Storage::extend('google', function($app, $config) {
                $options = [];

                if (!empty($config['teamDriveId'] ?? null)) {
                    $options['teamDriveId'] = $config['teamDriveId'];
                }

                if (!empty($config['sharedFolderId'] ?? null)) {
                    $options['sharedFolderId'] = $config['sharedFolderId'];
                }

                $client = new \Google\Client();
                $client->setClientId($config['clientId']);
                $client->setClientSecret($config['clientSecret']);
                $client->refreshToken($config['refreshToken']);
                
                $service = new \Google\Service\Drive($client);
                $adapter = new \Masbug\Flysystem\GoogleDriveAdapter($service, $config['folder'] ?? '/', $options);
                $driver = new \League\Flysystem\Filesystem($adapter);

                return new \Illuminate\Filesystem\FilesystemAdapter($driver, $adapter);
            });
        } catch(\Exception $e) {
            // your exception handling logic
        }
        // ...
    }
    // ...
}

现在您可以通过这种方式访问驱动器

$googleDisk = Storage::disk('google');
//$secondDisk = Storage::disk('second_google'); //others disks

请注意,只能有一个默认云存储驱动器,由您 .env(或配置)文件中的 FILESYSTEM_CLOUD 定义。如果您将其设置为 google,则将成为云驱动器

Storage::cloud(); // refers to Storage::disk('google')

限制

将显示路径用作文件夹和文件的标识符需要它们是唯一的。不幸的是,Google Drive 允许用户创建具有相同(显示)名称的文件和文件夹。在这种情况下,当无法确定唯一路径时,此适配器选择最旧的(第一个)实例。如果较新的重复项是文件夹,并且用户在内部放置一个唯一的文件或文件夹,则适配器将能够正确访问它(因为完整路径是唯一的)。

同一 Google Drive 的并发使用可能会由于文件/文件夹标识符和文件对象的大量缓存而导致意外问题。

致谢

此适配器基于 Naoki Sawada 的优秀 flysystem-google-drive

它还包含 Google 的 Google_Http_MediaFileUpload 的改编。我添加了对从流中直接进行可恢复上传的支持(避免将数据复制到内存中)。

TeamDrive 的实现由 Maximilian Ruta - Deltachaos 完成。

为 Flysystem V2 重写适配器以及各种修复由 Erik Niebla - erikn69 实现。