ファイルをローカルディスクにアップロードする際の公開設定について調べてみました。
アップロードしたファイルだけでなく、ディレクトリのパーミッションが意図した値になっているか注意が必要です。
調べた時のバージョンは下記のとおりです。
・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]


