koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

LaravelのFilesystem S3アダプタ管理下のオブジェクトをバケット間コピー

Laravelには色んな種類のファイルシステムを操作する便利機能が用意されていて当然その対象にはS3も含まれている。

laravel.com

ちょっとS3バケット間でファイルをコピーしたいと思った。

こんなメソッドシグネチャで考える。

<?php
function copyOverBucket($fromDiskName, $fromPath, $toDickName, $toPath);

LaravelのFileSystem(実体は File Storage Abstraction for PHP - Flysystem )には汎用的なファイル操作APIが提供されている。

そこに別のFileSystemインスタンスとの間のファイルコピーはない。

したがって、素直にコピーしようとするとこのようになる。

<?php
function copyOverBucket($fromDiskName, $fromPath, $toDickName, $toPath) {
  Storage::disk($toDiskName)->put($toPath, Storage::disk($fromDiskName)->get($fromPath));
}

ダウンロードしてアップロード

そうするとコピー元のS3バケットから一度オブジェクトをダウンロードして、それをコピー先のS3バケットにアップロードする動きになる。

(実際には大して問題にならないレイテンシや転送量のコストでも気分的に) 嫌だ。

バケット間で直接コピーしたい。

PHPSDKを直接使用したサンプルは提示されている。

docs.aws.amazon.com

Aws S3 (v3) Adapter - Flysystem の実装も参考になりそうだ。

github.com

とりあえず動きはした。

Aws S3 (v3) Adapterはしっかり実装が隠蔽されていて無理やりな感じになってしまった。

汎用的なファイル操作APIとS3固有のAPIでの操作を混在させるのは筋が悪いかもしれない。参考程度に見てください。

<?php
function copyOverBucket($fromDiskName, $fromPath, $toDickName, $toPath) {
        // AwsS3V3Adapter がプレフィクサを公開していないので実装に合わせて処理する
        $fromDisk = Storage::disk($fromDiskName);
        $fromPrefixer = new PathPrefixer($fromDisk->getConfig()['path'] ?? '');
        $toDisk = Storage::disk($toDiskName);
        $toPrefixer = new PathPrefixer($toDisk ->getConfig()['path'] ?? '');

        // S3ディスクの実体はちょっと拡張されたラッパーなのでS3のクライアントを取得できる
        $toDisk->getClient()->copyObject([
            'Bucket'     => $toDisk->getConfig()['bucket'],
            'Key'        => $toPrefixer->prefixPath($toPath),
            'CopySource' => "{$fromDisk->getConfig()['bucket']}/{$fromPrefixer->prefixPath($fromPath)}",
        ]);
}

バケット間でコピー!

S3ディスクの実体: AwsS3V3Adapterでラップされている。

他方、S3のバケット名などを取得するインタフェースはない*1。そのため各ディスクの設定を参照することになる。設定なのでそうそう変わるものではないだろうが、内部構造や実装が変化したら容易に破損するだろうからあまりいい作りではないように思える*2

とは言え、目的は達成できたのでまぁ良かった。

そんな感じ。

*1:v1の頃はできた模様 https://github.com/thephpleague/flysystem-aws-s3-v3/blob/4e25cc0582a36a786c31115e419c6e40498f6972/src/AwsS3Adapter.php#L91-L99

*2:抽象化されたファイル操作を行えるのが利点の機能を完全に否定しているし、ローカルとクラウドでファイルの保存先を変えるようなこともできなくなる。