必须清缓存、验证配置加载与驱动绑定:运行php artisan config:clear,再在tinker中执行config('filesystems.disks.public.root')确认路径,dd(Storage::disk('public')->getDriver()->getAdapter())验证驱动实例,否则配置不生效。

要让Laravel真正按你预期的方式存取文件,必须同时打通Storage门面调用路径、磁盘配置生效机制、底层驱动加载逻辑三道关卡;只改.env或只动config/filesystems.php,90%概率文件存错位置或根本报错。
确认当前生效的磁盘配置
第一步:运行php artisan tinker进入交互环境。
第二步:执行config('filesystems.disks.public.root'),查看返回值是否为/var/www/laravel/storage/app/public这类绝对路径——若返回null或空字符串,说明配置根本没加载。
第三步:执行dd(Storage::disk('public')->getDriver()->getAdapter()),能打印出League\Flysystem\Local\LocalFilesystemAdapter实例才代表驱动已正确绑定;如果报错Class not found,就是缺Flysystem依赖或Adapter未注册。
【关键前提】所有配置修改后必须清缓存:php artisan config:clear,否则Laravel永远读的是旧缓存值。
本地磁盘(local)权限与路径验证
直接在控制器里写Storage::disk('local')->put('test.txt', 'ok'),报failed to open stream?这不是代码写错了,而是目录不存在或权限卡死。
方法一:检查storage/app/是否存在且可写
运行ls -ld storage/app,输出中第三段应含rwx,用户组名需匹配Web服务器进程(如www-data)。若显示drw-r--r--,立刻执行chmod 775 storage/app。
方法二:验证PHP进程能否真实写入
新建一个测试脚本touch.php,内容为,用浏览器访问该脚本。若文件没生成,说明Web用户对storage/app无写权限,【此时改config/filesystems.php毫无意义】。
public磁盘文件如何被网页访问
public磁盘默认存到storage/app/public/,但Web根目录下没有这个路径——所以必须建软链接。
运行php artisan storage:link,它会在public/storage创建指向../storage/app/public的符号链接。
前端引用时用{{ asset('storage/avatar.jpg') }},实际请求路径是/storage/avatar.jpg,Nginx/Apache会自动映射到public/storage/avatar.jpg→storage/app/public/avatar.jpg。
注意:如果手动把文件放进storage/app/public/但页面404,先检查public/storage是否存在且是有效链接——用ls -l public/storage看箭头指向是否正确。
S3兼容服务(OSS/COS)配置避坑
填完阿里云OSS的AccessKey和Endpoint,Storage::disk('s3')->put()仍报InvalidAccessKeyId?问题不在密钥本身,而在签名协议不匹配。
方法一:强制启用Path Style Endpoint
在.env中加一行:AWS_USE_PATH_STYLE_ENDPOINT=true。不加这句,Laravel会拼bucket.oss-cn-hangzhou.aliyuncs.com,而OSS只认oss-cn-hangzhou.aliyuncs.com/bucket格式。
方法二:Region字段必须带前缀
AWS_DEFAULT_REGION=oss-cn-hangzhou(不是cn-hangzhou),少oss-就认证失败。
方法三:Endpoint必须带协议且不含bucket
AWS_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com,不能写成https://my-bucket.oss-cn-hangzhou.aliyuncs.com或漏掉https://。
验证是否通:进tinker执行Storage::disk('s3')->getDriver()->getAdapter()->getClient()->listBuckets(),返回Bucket列表才算真正连通。
切换磁盘并安全存储上传文件
第一步:表单必须带enctype="multipart/form-data",否则$request->file()永远为null。
第二步:接收文件后立即验证有效性
if ($request->file('photo')->isValid()) { ... },跳过这步可能导致恶意文件绕过后续校验。
第三步:用storeAs()指定精确路径和文件名
$path = $request->file('photo')->storeAs('avatars', $user->id . '.jpg', 'public');,第三个参数'public'明确指定磁盘,避免依赖默认配置。
第四步:数据库只存相对路径
存avatars/123.jpg而非/var/www/storage/app/public/avatars/123.jpg,这样换服务器或改磁盘配置时路径逻辑不受影响。











