📝

LaravelのファイルストレージにS3を使用したときにurlメソッドでファイルのURLが取得できない原因と対策

2021/04/28に公開

概要

LaravelのファイルストレージでS3ドライバを使用しているとき、下記のようなコードで指定したファイルのURLを取得できるはずが相対パスが返ってきた場合の原因と解決法をメモ。

// Laravelのファイルストレージのページから引用
use Illuminate\Support\Facades\Storage;
$url = Storage::url('file.jpg');

バージョン

  • Laravel Framework 8.10.0

原因

s3ドライバーを使用している場合は、完全修飾リモートURLが返されます。

Laravelのドキュメントにこのように記載してあるのでどんな実装になっているのかurlメソッドを調査。/Illuminate/Filesystem/FilesystemAdapter.phpurlメソッドがあるので読んでみる。

/**
 * Get the URL for the file at the given path.
 *
 * @param  string  $path
 * @return string
 *
 * @throws \RuntimeException
 */
public function url($path)
{
    $adapter = $this->driver->getAdapter();

    if ($adapter instanceof CachedAdapter) {
        $adapter = $adapter->getAdapter();
    }

    if (method_exists($adapter, 'getUrl')) {
        return $adapter->getUrl($path);
    } elseif (method_exists($this->driver, 'getUrl')) {
        return $this->driver->getUrl($path);
    } elseif ($adapter instanceof AwsS3Adapter) {
        return $this->getAwsUrl($adapter, $path);
    } elseif ($adapter instanceof Ftp || $adapter instanceof Sftp) {
        return $this->getFtpUrl($path);
    } elseif ($adapter instanceof LocalAdapter) {
        return $this->getLocalUrl($path);
    } else {
        throw new RuntimeException('This driver does not support retrieving URLs.');
    }
}

S3ドライバの時はgetAwsUrlメソッドが呼ばれるので追ってみる。

/**
 * Get the URL for the file at the given path.
 *
 * @param  \League\Flysystem\AwsS3v3\AwsS3Adapter  $adapter
 * @param  string  $path
 * @return string
 */
protected function getAwsUrl($adapter, $path)
{
    // If an explicit base URL has been set on the disk configuration then we will use
    // it as the base URL instead of the default path. This allows the developer to
    // have full control over the base path for this filesystem's generated URLs.
    if (! is_null($url = $this->driver->getConfig()->get('url'))) {
        return $this->concatPathToUrl($url, $adapter->getPathPrefix().$path);
    }

    return $adapter->getClient()->getObjectUrl(
        $adapter->getBucket(), $adapter->getPathPrefix().$path
    );
}

下記のコードからS3ドライバの設定でurlを指定しているとバケットにあるオブジェクトのURLが返ってこないことが判明。

// getAwsUrlメソッドから抜粋
if (! is_null($url = $this->driver->getConfig()->get('url'))) {
    return $this->concatPathToUrl($url, $adapter->getPathPrefix().$path);
}

解決策

Laravelのconfig/filesystems.phpでS3ドライバのデフォルトは下記のようになっているので

's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => env('AWS_URL'),
    'endpoint' => env('AWS_ENDPOINT'),
],

'url' => null,と変更。

's3' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_BUCKET'),
    'url' => null,
    'endpoint' => env('AWS_ENDPOINT'),
],

おわりに

設定項目があるからと適当にAWS_URLという環境変数と値を設定してしまったのが原因なのできちんと必要なものだけ設定せねばなと。

参考

Discussion