ThinkPHP 6 Ajax上传文件需前端用FormData发送并自动设置multipart/form-data头,后端用request()->file('key')获取,校验扩展名与大小,通过Filesystem::disk('public')->putFile安全保存,返回URL应使用Url::build()而非拼接服务器路径。

ThinkPHP 6 里用 Ajax 上传文件,request()->file() 拿不到文件?
不是框架不支持,是前端没发对、后端没接住。ThinkPHP 的 request()->file() 只认标准的 multipart/form-data 表单字段,不解析 application/json 或 base64 字符串。
- 确保前端用
FormData构造请求,而不是JSON.stringify()发 raw 数据 -
axios要显式删掉Content-Type头(让它自动设为带 boundary 的 multipart) - 后端控制器里别用
input('file')—— 这是取字符串参数的,得用request()->file('upload'),其中'upload'必须和前端FormData.append('upload', file)的 key 一致 - 如果用了 Nginx,检查是否拦截了大文件:
client_max_body_size默认常为 1MB,上传失败时浏览器可能只报net::ERR_CONNECTION_RESET,后端却完全收不到请求
上传成功后怎么在 ThinkPHP 6 里安全保存并返回路径?
别直接用 $file->move() 到 public 目录下就完事——路径拼错、重名覆盖、恶意扩展名都可能出问题。
- 用
$file->validate(['size'=>1024*1024*2, 'ext'=>'jpg,png,gif'])做基础校验,注意ext不区分大小写,但validate()不校验 MIME 类型,需额外用finfo_file()补充 - 保存路径建议用
think\facade\Filesystem::disk('public')->putFile('uploads', $file),它自动处理唯一文件名、目录创建和驱动适配(本地/oss 都能切) - 返回给前端的 URL 别拼
$_SERVER['HTTP_HOST']—— 用Url::build()或think\facade\Url::image()(如果配了 image 驱动),避免 HTTPS/HOST 不一致导致 404 - 别把临时文件路径(
$file->getRealPath())直接 echo 出去,那是服务器内部路径,前端访问不到
为什么 Ajax 上传后接口返回 500,但日志里没错误?
常见于 PHP 配置或 ThinkPHP 中间件拦截,错误被静默吞掉。
- 检查
php.ini:确认file_uploads = On、upload_max_filesize和post_max_size都大于你要传的文件 - ThinkPHP 的
ValidateToken中间件默认会校验 POST 请求的 token,但 Ajax 上传通常不带 token —— 在路由定义里关掉:->middleware('token', false) - 如果用了多层反向代理(如 Nginx → Apache),
$_FILES可能为空,因为某些代理会剥离 multipart body;优先统一用 Nginx 直接转发 PHP-FPM - 开启
app_debug = true并查看runtime/log下当天的日志文件,搜索UPLOAD_ERR_开头的错误码(比如UPLOAD_ERR_INI_SIZE)
想让上传和后续处理(比如生成缩略图)不卡住 Ajax 响应?
ThinkPHP 本身没有内置异步任务队列,直接在上传接口里调 imagecreatefromxxx() 属于同步阻塞,用户会感知明显延迟。
立即学习“PHP免费学习笔记(深入)”;
- 简单场景:把耗时操作扔到
cli命令里,用exec("nohup php think task:process {$fileId} > /dev/null 2>&1 &")启动后台进程(注意权限和路径) - 推荐方案:接入
think-queue+ Redis,上传成功后Job::dispatch(['file_id' => $id]),由独立 worker 进程处理,前端轮询或 WebSocket 接收结果 - 别在队列任务里用
request()或session()—— 它们只存在于 HTTP 生命周期中,队列是 CLI 环境,要用数据库或缓存传递上下文 - 上传接口本身应快速返回「已接收,处理中」,状态字段存在数据库里,避免用户重复提交
异步上传真正的难点不在 TP 代码怎么写,而在边界情况的兜底:断网重传、同一文件多次提交、上传中途刷新页面、CDN 缓存了旧的 JS 导致 FormData 构造方式不兼容……这些没法靠一个 move() 解决。


















