Chào mọi người, như mọi người đều biết đến Queue ở trong môn học Cấu trúc dữ liệu và giải thuật. Bài viết hôm nay mình xin chia sẻ đến với moị người việc áp dụng Queue ở trong Laravel và cụ thể là với công việc lưu trữ file trên Google Drive.
-
Cài đặt Project Laravel (ở thời điểm mình viết bài là Laravel 5.7):
composer create-project --prefer-dist laravel/laravel google-drive-demo
-
Cài đặt pakage để tương tác với Google Drive - Flysystem Google Drive :
composer require nao-pon/flysystem-google-drive:~1.1 (For Google Drive API V3)
composer require nao-pon/flysystem-google-drive:~1.0.0 (For Google Drive API V2)
Ở đây mình sẽ dùng với Google Drive API v3
1. Tạo Adapter và Service Provider của Google Drive
- Tạo 2 file GoogleDriveAdapter.php và GoogleDriveServiceProvider.php trong thư mục app/Providers:
GoogleDriveAdaper.php:
<?php namespace AppProviders; class GoogleDriveAdapter extends HypwebFlysystemGoogleDriveGoogleDriveAdapter { public function getService() { return $this->service; } }
GoogleDriveServiceProvider.php:
<?php namespace AppProviders; use IlluminateSupportServiceProvider; class GoogleDriveServiceProvider extends ServiceProvider { /** * Bootstrap the application services. * * @return void */ public function boot() { Storage::extend('google', function ($app, $config) { $client = new Google_Client(); $client->setClientId($config['clientId']); $client->setClientSecret($config['clientSecret']); $client->refreshToken($config['refreshToken']); $service = new Google_Service_Drive($client); $options = []; if (isset($config['teamDriveId'])) { $options['teamDriveId'] = $config['teamDriveId']; } $adapter = new GoogleDriveAdapter($service, $config['folderId'], $options); return new LeagueFlysystemFilesystem($adapter); }); } /** * Register the application services. * * @return void */ public function register() { // } }
- Khai báo GoogleDriveServiceProvider vào providers trong config/app.php:
'providers' => [ /* * Laravel Framework Service Providers... */ ... /* * Package Service Providers... */ ... /* * Application Service Providers... */ AppProvidersGoogleDriveServiceProvider::class, ],
- Vì ở đây ta coi Google Drive như là 1 disk nên ta khai báo google disk vào trong config/filesystems.php:
<?php return [ 'default' => env('FILESYSTEM_DRIVER', 'cloud'), 'cloud' => env('FILESYSTEM_CLOUD', 'google'), 'disks' => [ /*...*/ 'google' => [ 'driver' => 'google', 'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'), 'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'), 'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'), 'folderId' => env('GOOGLE_DRIVE_FOLDER_ID'), ], ], ];
2. Lấy Google Drive API Key
a. Client ID & Secret:
- Truy cập vào Google Console và tạo 1 App: https://console.developers.google.com
- Ở đây mình đã tạo xong app google-drive-demo, vào Library tìm Google Drive API và enable:
- Sau đó, vào Credentials, tạo OAuth client ID: Tại phần này chúng ta nhớ để trong phần Authorized redirect URIs là https://developers.google.com/oauthplayground. Sau khi tạo xong, chúng ta sẽ có client ID và client secret
b. Refresh Token:
- Truy cập vào đường dẫn: https://developers.google.com/oauthplayground, ở góc bên phải phần cài đặt ta dán client ID và client secret:
- Sau đó, ở bên trái, tìm và chọn Google Drive API V3, chọn dòng đầu tiên:
- Tiếp tục ta làm theo các bước như hình bên dưới:
Cuối cùng ta lấy được Refresh token.
c. Lấy ID thư mục lưu trữ (Root):
- Truy cập vào Google Drive của mình và tạo một thư mục, ở đây mình sẽ tạo thư mục và lấy tên là Demo, đây là thư mục mà chúng ta sẽ tương tác, ID chính là phần cuối trong URL như hình:
Sau khi lấy được tất cả các thông tin trên, chúng ta paste vào file .env :
FILESYSTEM_CLOUD = google GOOGLE_DRIVE_CLIENT_ID = *** GOOGLE_DRIVE_CLIENT_SECRET = *** GOOGLE_DRIVE_REFRESH_TOKEN = *** GOOGLE_DRIVE_FOLDER_ID = ***
Xong, bây giờ chúng ta có thể truy cập Google Drive như một disk của Storage rồi.
3. Tạo file
- Đầu tiên chúng ta tạo file demo.blade.php trong resources/views để Upload File:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Google Drive Demo</title> </head> <body> <form action="{{ route('post.file') }}" method="POST"> <input type="file" name="file"> <br><br> <button type="submit">Upload</button> </form> </body> </html>
- Tạo DemoController.php: php artisan make:controller DemoController
<?php namespace AppHttpControllers; use IlluminateHttpRequest; class DemoController extends Controller { public function store(Request $request) { $name = $request->file->getClientOriginalName(); $request->file->storeAs('/', $name, 'google'); return 'File was saved to Google Drive'; } }
- File web.php:
<?php Route::get('/', function () { return view('demo'); }); Route::post('/', '[email protected]')->name('post.file');
Bây giờ chúng ta thử Upload 1 file lên thử xem. Ổn, file đã được upload lên Google Drive, tuy nhiên ta có thể thấy rằng thời gian chờ đợi upload khoảng gần 3s cho 1 file ~2Mb. Do đó, việc sử dụng Queue Job ở đây để thực hiện việc upload file là một cách để giải quyết vấn đề trên.
4. Tạo Queue Job
- Để chạy queue job trong Laravel đã hỗ trợ kha khá driver, nhưng ở đây để đơn giản mình sẽ sử dụng database để lưu lại Job:
- Tạo table jobs:
- php artisan queue:table
- php artisan migrate
- Ở file .env chúng ta sửa QUEUE_CONNECTION=database
- Tạo Job: php artisan make:job PutFile, một file PutFile.php được tạo ở trong app/Jobs/, sửa lại nội dung như sau:
<?php namespace AppJobs; use IlluminateBusQueueable; use IlluminateQueueSerializesModels; use IlluminateQueueInteractsWithQueue; use IlluminateContractsQueueShouldQueue; use IlluminateFoundationBusDispatchable; use IlluminateSupportFacadesStorage; class PutFile implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; protected $name; /** * Create a new job instance. * * @return void */ public function __construct($name) { $this->name = $name; } /** * Execute the job. * * @return void */ public function handle() { $filePut = Storage::disk('public')->get('/' . $this->name); Storage::cloud()->put('/' . $this->name, $filePut); Storage::disk('public')->delete('/' . $this->name); } }
- Sửa lại file DemoController.php:
<?php namespace AppHttpControllers; use IlluminateHttpRequest; use AppJobsPutFile; class DemoController extends Controller { public function store(Request $request) { $name = $request->file->getClientOriginalName(); $request->file->storeAs('/', $name, 'public'); PutFile::dispatch($name); return 'File was saved to Google Drive'; } }
Xong! Bây giờ chúng ta mở Terminal và chạy php artisan queue:work. Upload lại thử file và quan sát Job đã được đẩy vào trong hàng đợi và bạn sẽ thấy web được load nhanh hơn.
Giải thích 1 chút: Do Job không cho phép truyền trực tiếp đối tượng $request->file nên ở đây mình upload file lên disk local là public, sau đó ở trong Job mình mới lấy ra file ở local upload lên Google Drive, và cuối cùng là xóa file ở disk local đi.
Trong bài viết này, mình đã chia sẻ đến mọi người cách để lưu trữ file trên Google Drive kết hợp dùng Queue Job. Bài viết này mình mới chỉ dừng ở mức demo rất đơn giản, hi vọng sẽ giúp ích 1 phần nào đó cho các bạn. Ngoài việc lưu trữ file các bạn có thể tìm hiểu nhiều hơn với các thao tác xóa file, hay các thao tác với thư mục,...tại đây. Ngoài ra các bạn cũng có thể thao tác với các Cloud khác như Amazone S3, Azure, Dropbox, ...
BKFA Team
Laravel & Google Drive Storage
Filesystem abstraction for PHP
Laravel Queue