
本文详解 laravel 中使用 `storage:link` 后仍无法通过 `asset()` 加载 `storage/app/public/` 下图片的常见原因,并提供完整解决方案,包括路径修正、url 生成逻辑、权限检查及安全建议。
在 Laravel 中,将用户上传或 Seeder 生成的图片存入 storage/app/public/ 目录(如 storage/app/public/users/xxx.jpg)是标准做法,但直接用 asset('storage/users/xxx.jpg') 加载会失败——这不是路径写错,而是对 Laravel 资源机制的理解偏差。
? 根本原因:asset() 只解析 public/ 目录下的文件
Laravel 的 asset() 辅助函数仅支持从 public/ 目录及其子目录中引用静态资源(如 CSS、JS、图片)。它不会穿透到 storage/ 目录,即使你已执行 php artisan storage:link 创建了符号链接(该命令本质是将 public/storage → storage/app/public),正确的访问路径也必须以 storage/ 开头(即 public/storage/...),而非 storage/...。
你当前的 img 标签:
@@##@@image) }}" ...>
生成的 URL 是类似 /storage/users/123.jpg —— 这看似正确,实则缺失关键前缀。因为 asset() 生成的是相对于 public/ 的 URL,而 storage:link 创建的符号链接位于 public/storage/,所以实际应访问的是 /storage/...(即 public/storage/...),但你的 $user->image 值很可能包含冗余路径。
✅ 正确做法:确保路径纯净 + 使用 Storage::url()
1. 工厂/Seeder 中保存路径要「相对」且「无前缀」
// ❌ 错误:faker->image() 写入绝对路径,且含 'storage/app/public/'
'image' => $this->faker->image('storage/app/public/users', 140, 180, null, false),
// ✅ 正确:只指定相对子路径(Laravel 自动存入 storage/app/public/)
'image' => $this->faker->image('users', 140, 180, null, false), // 生成如: users/abc.jpg这样数据库中存储的 image 字段值就是 users/abc.jpg,不带 storage/app/public/ 前缀。
2. Blade 中使用 Storage::url() 生成安全 URL(推荐)
@use('Illuminate\Support\Facades\Storage')
@@##@@image) }}"
class="avatar avatar-sm me-3 border-radius-lg"
alt="user avatar">✅ Storage::url() 会自动拼接为 /storage/users/abc.jpg(对应 public/storage/users/abc.jpg 符号链接),且支持云存储驱动无缝切换。
? 补充:若坚持用 asset(),需手动补全 storage/ 前缀,且确保 $user->image 不含重复路径:
3. 验证符号链接是否生效
运行以下命令确认链接存在且可读:
ls -la public/storage # 应输出类似:storage -> ../storage/app/public
同时检查 storage/app/public/users/ 下图片是否存在,且 Web 服务器(Nginx/Apache)有读取权限。
⚠️ 注意事项与最佳实践
- 不要将图片直接存入 public/:违背 Laravel 安全约定,storage/app/public/ + storage:link 才是官方推荐方式;
- 避免硬编码路径:使用 Storage::url() 或 Storage::disk('public')->url() 更健壮;
- Seeder 中确保磁盘配置正确:确认 config/filesystems.php 中 public 磁盘的 root 指向 storage_path('app/public');
- 生产环境注意缓存:修改后运行 php artisan config:clear 和 php artisan view:clear。
✅ 总结
问题核心在于混淆了 asset() 的作用域(仅限 public/ 目录)与 storage:link 的映射逻辑(public/storage/ → storage/app/public/)。只需两步即可解决:
① 工厂中用 faker->image('users', ...) 保存相对路径;
② Blade 中用 Storage::url($user->image) 生成 URL。
从此告别 404,让头像稳稳加载。










