ファイルをローカルディスクにアップロードする際の公開設定について調べてみました。
アップロードしたファイルだけでなく、ディレクトリのパーミッションが意図した値になっているか注意が必要です。
調べた時のバージョンは下記のとおりです。
・PHP:8.3.7
・laravel/framework:v11.7.0
困ったこと
下記の公式のドキュメントにあるstorePublicly
を利用して保存をしたところ、ディレクトリのパーミッションが意図しない値(private
相当の0600
)になってしまいました。
何が起きた?
設定の状態は下記の状態。
・config/filesystems.php
:手を加えずデフォルトのまま
・.env
:FILESYSTEM_DISK=local
の状態です
storePublicly
関数でファイルを保存したところ、アップロードされたファイルのパーミッションはちゃんと0644
になっていたのですが、files
ディレクトリのパーミッションが0600
になってしまいました。
ディレクトリのパーミッションは0755
を期待していました。
$request->file("file")->storePublicly('public/files');
どうしたらよかった?
storePublicly
関数ではなく、store
関数を利用して第2引数にdisk
の設定(public
)を渡すことで、ディレクトリのパーミッションは0755
になります。
// 同じ結果になります
$request->file("file")->store('files', 'public');
$request->file("file")->store('files', ['disk' => 'public']);
処理を追ったところ、Illuminate\Filesystem\FilesystemManager:resolve()
の時にconfig('filesystems.disks.public')
が取得されます。
この設定からファイル操作のドライバーに'visibility' => 'public'
が渡されることで、ディレクトリのパーミッションがpublic
相当の0755
となります。
storePublicly は何をしている?
storePublicly
関数はアップロードしたファイル自身のみのパーミッションをpublic
にするだけであり、ディレクトリのパーミッションはprivate
相当の0600
で生成されます。
storePublicly
関数の実装を見ると$options['visibility'] = 'public';
しています。
なんだか、storePublicly
関数でもイケそうに見えますね。(でもダメなんですけどね)
public function storePublicly($path = '', $options = [])
{
$options = $this->parseOptions($options);
$options['visibility'] = 'public';
return $this->storeAs($path, $this->hashName(), $options);
}
参考:https://github.com/laravel/framework/blob/v11.7.0/src/Illuminate/Http/UploadedFile.php#L46-L53
処理を追ってみたところstorePublicly
関数の$options['visibility'] = 'public';
はdisk
の値には関係せず、config('filesystems.disks.local')
の設定が取得されます。
最終的に、FlysystemライブラリのLeague\Flysystem\Local\LocalFilesystemAdapter:writeToFile()
に渡ります。
参考:https://github.com/thephpleague/flysystem-local/blob/3.25.1/LocalFilesystemAdapter.php#L118-L135$config->get(Config::OPTION_VISIBILITY)
のところで$options['visibility'] = 'public';
が利用され、ファイルのパーミッションがpublic
相当の0755
になります。
ディレクトリのパーミッションはどうするかというと、$config->get(Config::OPTION_DIRECTORY_VISIBILITY)
の部分です。Config::OPTION_DIRECTORY_VISIBILITY
は'directory_visibility'
なので、$options
に設定したら行けそうですね。
まとめると、disk
がlocal
の状態でファイルアップロードをするが、ファイルとディレクトリのパーミッションをpublic
にしたい場合(そんなことある?)は下記のようにできます。
$request->file("file")->store('public/files', ['visibility' => 'public', 'directory_visibility' => 'public']);
[AD]