Files
CFDivePlatform/app/Console/Commands/MigrateStorageToS3.php
T
a620906209 54178f6d70 feat(storage): S3 相容物件儲存支援 + 一次性遷移指令
實作 cloud-ready-s3-storage 的程式碼部分(tasks 1-3):

- config/filesystems.php:public disk 的 driver 改吃 FILESYSTEM_DISK env,
  切換為 s3 時不需改動既有 6 個 Storage::disk('public') 呼叫點
- .env.example:FILESYSTEM_DISK 維持 local 預設(本機無 S3 服務),補齊
  AWS_ENDPOINT/AWS_URL/AWS_USE_PATH_STYLE_ENDPOINT 供 R2 等服務使用
- 新增 storage:migrate-to-s3 指令:一次性把 storage/app/public 既有檔案
  上傳到 S3,來源固定讀本機磁碟(不受 FILESYSTEM_DISK 影響),支援
  --dry-run,失敗不中斷且不刪除本機檔案
- 新增測試:遷移指令(上傳/dry-run/部分失敗)+ public disk 在 local
  driver 下零 AWS credentials 也能正常運作

239 tests passed / 578 assertions,容器內驗證無回歸。

R2 環境準備、VPS 部署與實際切換(tasks 4-6)待 Hank 自行處理。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011LpcY9b7y9x4fusHBTXciQ
2026-08-03 03:35:24 +08:00

84 lines
2.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Throwable;
class MigrateStorageToS3 extends Command
{
protected $signature = 'storage:migrate-to-s3
{--dry-run : 只列出將上傳的檔案與總數,不實際執行}';
protected $description = '將本機 storage/app/public 下既有上傳檔案搬遷至 S3 相容物件儲存(public disk 切換前的一次性遷移,不刪除本機檔案)';
public function handle(): int
{
// 直接指定 local 磁碟為來源,不透過會依 FILESYSTEM_DISK 變動的 'public' disk
// 避免這支指令在 FILESYSTEM_DISK 已切成 s3 後誤把來源當成目的地。
$source = Storage::build([
'driver' => 'local',
'root' => storage_path('app/public'),
]);
$files = $source->allFiles();
if (empty($files)) {
$this->info('storage/app/public 底下沒有檔案,無需遷移。');
return self::SUCCESS;
}
if ($this->option('dry-run')) {
$this->info(sprintf('[dry-run] 將上傳 %d 個檔案:', count($files)));
foreach ($files as $file) {
$this->line(" - {$file}");
}
return self::SUCCESS;
}
$destination = Storage::disk('s3');
$failed = [];
$this->info(sprintf('開始上傳 %d 個檔案至 S3...', count($files)));
$bar = $this->output->createProgressBar(count($files));
foreach ($files as $file) {
try {
$destination->put($file, $source->get($file));
if (! $destination->exists($file)) {
$failed[] = $file;
}
} catch (Throwable $e) {
$failed[] = $file;
$this->newLine();
$this->warn("上傳失敗:{$file}{$e->getMessage()}");
}
$bar->advance();
}
$bar->finish();
$this->newLine(2);
$succeeded = count($files) - count($failed);
$this->info(sprintf('完成:%d/%d 個檔案上傳成功。', $succeeded, count($files)));
if (! empty($failed)) {
$this->error('以下檔案上傳失敗,本機檔案未刪除,可重新執行本指令補上傳:');
foreach ($failed as $file) {
$this->line(" - {$file}");
}
return self::FAILURE;
}
return self::SUCCESS;
}
}