Webman默认仅服务public目录,要使/upload/下图片可访问,需在StaticFile中间件中拦截路径、检查文件存在、构造Response并设Content-Type与缓存头;或在Nginx中用location ^~ /upload/ alias直接服务以避免PHP中转性能损耗。

Webman 默认只服务 public/ 目录下的静态文件,想让上传的图片、附件等非 public 目录里的资源被前端访问,必须手动配置额外的静态资源映射 —— 否则前端请求会 404,不是路径写错,是根本没被框架接管。
如何让 /upload/ 下的图片可被 HTTP 访问
Webman 不像 Laravel 那样自动 symlink 或内置多目录静态服务,它默认只认 public/。如果你把用户上传的图片存到 storage/upload/ 或 /data/uploads/ 这类目录,前端直接请求 /upload/xxx.jpg 是无效的。
正确做法是在 app/middleware/StaticFile.php 中扩展逻辑,拦截特定路径并手动读取文件:
- 在
process()方法里加判断:if (preg_match('#^/upload/.*\.(jpg|jpeg|png|gif|webp)$#i', $request->path())) - 用
file_exists()检查物理路径,例如/data/uploads/xxx.jpg - 用
readfile()输出内容,并手动设置Content-Type和Content-Length - 务必加
exit;或返回Response对象,避免后续中间件或路由继续执行
StaticFile 中间件的缓存与跨域头怎么加才生效
直接在 $response->withHeaders() 后 return 是无效的 —— 因为 $next($request) 已经走完,此时 $response 可能是 404 或空响应。你得在真正生成响应前插入头信息。
立即学习“PHP免费学习笔记(深入)”;
推荐写法:在确认要返回文件时,构造完整 Response:
return response()->withBody(new \Webman\Http\Stream(fopen($real_path, 'rb')))
->withHeader('Content-Type', 'image/jpeg')
->withHeader('Cache-Control', 'public, max-age=86400')
->withHeader('Access-Control-Allow-Origin', '*');
-
max-age=86400表示浏览器缓存 1 天,比默认不缓存更省带宽 - 不要用
withHeaders([])批量设,容易覆盖掉Content-Type导致图片显示为乱码 - 如果启用了 HTTPS,
Access-Control-Allow-Origin: *不能和Credentials: true共存,需按需调整
为什么 Nginx 转发 /upload/ 到 index.php 会导致性能暴跌
常见错误是把 Nginx 的 try_files 写成 try_files $uri $uri/ /index.php?$args,结果所有 /upload/xxx.jpg 请求都被转发给 PHP,哪怕文件真实存在。
后果很直接:php-fpm 或 Webman Worker 进程去读磁盘、构造 Response、走中间件 —— 完全绕过操作系统级的 sendfile 优化,吞吐下降 5–10 倍。
- 正确做法:Nginx 层直接服务静态资源,只把动态请求交给 Webman
- 在 Nginx 配置中单独加一个
location ^~ /upload/块,用alias指向真实目录,并开启sendfile on - 确保该 location 块在
location /之前,避免被兜底规则捕获 - 如果必须由 Webman 统一处理(比如要鉴权),那就关掉 Nginx 的静态服务,但务必在中间件里用
ob_end_clean()+readfile(),别走框架响应构造流程
真正难的不是“能不能访问”,而是“谁来读文件”——Nginx 直接送还是 PHP 中转,决定了单机扛 1000 并发还是 10000 并发。选错一层,后面所有缓存、压缩、CDN 都白搭。



















