Created
February 21, 2025 18:37
-
-
Save godismyjudge95/f11d2a32de1c81f965ece015f92194f2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Console\Commands; | |
| use Illuminate\Console\Command; | |
| use Illuminate\Support\Facades\Storage; | |
| use Illuminate\Support\Facades\Config; | |
| use function Laravel\Prompts\select; | |
| class MigrateStorage extends Command | |
| { | |
| protected $signature = 'storage:migrate | |
| {source? : Source disk name} | |
| {destination? : Destination disk name} | |
| {--force : Force overwrite of existing files}'; | |
| protected $description = 'Migrate files from one storage disk to another using streaming'; | |
| protected $sourceDisk; | |
| protected $destinationDisk; | |
| protected $totalFiles = 0; | |
| protected $migratedFiles = 0; | |
| protected $skippedFiles = 0; | |
| public function handle() | |
| { | |
| // Get available disks from config | |
| $availableDisks = array_keys(Config::get('filesystems.disks', [])); | |
| if (empty($availableDisks)) { | |
| $this->error('No storage disks configured in config/filesystems.php'); | |
| return 1; | |
| } | |
| // Get source disk | |
| $sourceDiskName = $this->argument('source'); | |
| if (!empty($sourceDiskName) && !in_array($sourceDiskName, $availableDisks)) { | |
| $this->error("Invalid source disk: {$sourceDiskName}"); | |
| $sourceDiskName = ''; | |
| } | |
| if (empty($sourceDiskName)) { | |
| $sourceDiskName = select( | |
| 'Select source disk', | |
| $availableDisks, | |
| ); | |
| } | |
| $destinationDiskName = $this->argument('destination'); | |
| if (!empty($destinationDiskName) && !in_array($destinationDiskName, $availableDisks) || $destinationDiskName === $sourceDiskName) { | |
| $this->error("Invalid destination disk: {$destinationDiskName}"); | |
| $destinationDiskName = ''; | |
| } | |
| if (empty($destinationDiskName)) { | |
| // Filter out the source disk from available destinations | |
| $availableDestinations = collect($availableDisks) | |
| ->filter(fn($disk) => $disk !== $sourceDiskName) | |
| ->all(); | |
| if (empty($availableDestinations)) { | |
| $this->error('No available destination disks (must be different from source)'); | |
| return 1; | |
| } | |
| $destinationDiskName = select( | |
| 'Select destination disk', | |
| $availableDestinations, | |
| ); | |
| } | |
| $this->sourceDisk = Storage::disk($sourceDiskName); | |
| $this->destinationDisk = Storage::disk($destinationDiskName); | |
| $force = $this->option('force'); | |
| if (!$force && !$this->confirm("This will migrate all files from \"{$sourceDiskName}\" to \"{$destinationDiskName}\". Continue?")) { | |
| $this->info('Migration cancelled.'); | |
| return 0; | |
| } | |
| $this->info('Starting migration...'); | |
| $this->info('Scanning source directory...'); | |
| // Get all files recursively from source | |
| $files = $this->sourceDisk->allFiles(); | |
| $this->totalFiles = count($files); | |
| if ($this->totalFiles === 0) { | |
| $this->info("No files found in source disk \"{$sourceDiskName}\""); | |
| return 0; | |
| } | |
| $this->info("Found {$this->totalFiles} files to migrate."); | |
| $bar = $this->output->createProgressBar($this->totalFiles); | |
| foreach ($files as $filePath) { | |
| try { | |
| // Check if file already exists in destination | |
| if ($this->destinationDisk->exists($filePath) && !$force) { | |
| $this->skippedFiles++; | |
| $bar->advance(); | |
| continue; | |
| } | |
| // Ensure the directory exists | |
| $directory = dirname($filePath); | |
| if ($directory !== '.') { | |
| $this->destinationDisk->makeDirectory($directory); | |
| } | |
| // Stream the file from source to destination | |
| $sourceStream = $this->sourceDisk->readStream($filePath); | |
| if ($sourceStream === false) { | |
| throw new \Exception("Could not open source file: {$filePath}"); | |
| } | |
| $this->destinationDisk->writeStream($filePath, $sourceStream); | |
| // Clean up the stream | |
| if (is_resource($sourceStream)) { | |
| fclose($sourceStream); | |
| } | |
| $this->migratedFiles++; | |
| $bar->advance(); | |
| } catch (\Exception $e) { | |
| $this->error("Error migrating file: {$filePath}"); | |
| $this->error($e->getMessage()); | |
| // Clean up stream on error if it's still open | |
| if (isset($sourceStream) && is_resource($sourceStream)) { | |
| fclose($sourceStream); | |
| } | |
| } | |
| } | |
| $bar->finish(); | |
| $this->newLine(); | |
| $this->info('Migration completed!'); | |
| $this->info("Total files: {$this->totalFiles}"); | |
| $this->info("Migrated: {$this->migratedFiles}"); | |
| $this->info("Skipped: {$this->skippedFiles}"); | |
| return 0; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment