PHP 8.0 文件上传需严格校验 $_FILES['file']['error'] === UPLOAD_ERR_OK 再操作,下载须用 readfile() 配合三组正确 header;禁用 $_FILES['file']['type'],改用 finfo_open() 校验真实 MIME,防路径遍历与越权访问。

PHP 8.0 文件上传:$_FILES 的结构和校验必须做全
PHP 8.0 没有新增上传机制,但对 $_FILES 数组的类型声明更严格,直接访问未定义键会触发 TypeError(而非 PHP 7.x 的 Notice)。上传前务必检查 $_FILES['file']['error'] === UPLOAD_ERR_OK,否则后续操作可能崩溃。
常见错误现象:Undefined array key "tmp_name" 或 move_uploaded_file(): Unable to move —— 多数是没检查 error 值就直接用 tmp_name。
- 用
is_uploaded_file(<code>$_FILES['file']['tmp_name']) 判断是否为合法上传临时文件(不能只靠file_exists) - 校验
$_FILES['file']['size']是否超出upload_max_filesize和post_max_size(二者需同时调大) - 文件名要过滤:
basename(<code>$_FILES['file']['name']) 防路径遍历,再用pathinfo(..., PATHINFO_EXTENSION)提取扩展名做白名单校验(如in_array(strtolower($ext), ['jpg', 'pdf'])) - 目标目录需确保 PHP 进程有写权限,且
move_uploaded_file()的第二个参数必须是绝对路径(建议用__DIR__ . '/uploads/' . $safe_name)
PHP 8.0 下载文件:用 readfile() + 正确 header 组合
下载不是简单 echo file_get_contents(),尤其大文件会爆内存。PHP 8.0 对 header 输出更敏感,若之前已有输出(含空格、BOM、var_dump),会报 Cannot modify header information。
关键点在于三组 header 必须按顺序、无缓存、类型明确:
立即学习“PHP免费学习笔记(深入)”;
-
header('Content-Type: application/octet-stream');(通用二进制类型,避免浏览器解析) -
header('Content-Disposition: attachment; filename="' . rawurlencode($original_name) . '";');(rawurlencode处理中文名,attachment强制下载) -
header('Content-Length: ' . filesize($filepath));(必须准确,否则 Chrome 可能中断下载) - 最后调用
readfile(<code>$filepath) 流式输出,不经过内存缓冲
注意:readfile() 返回 int(字节数),但不用判断——只要 header 正确、文件可读,它就会把内容直接刷到响应体。
PHP 8.0 中上传/下载共用的坑:临时目录和超时配置
PHP 8.0 默认启用 opcache.preload,但不会预加载 upload_tmp_dir 目录。如果该目录被清理或权限变更,上传会静默失败(error 为 UPLOAD_ERR_NO_TMP_DIR)。
- 检查
phpinfo()中upload_tmp_dir路径是否存在、可写;若为空,PHP 会退回到系统默认 tmp(Linux 是/tmp,Windows 是C:\Windows\Temp) - 大文件上传时,
max_execution_time和max_input_time都可能超时(前者管脚本总耗时,后者管 POST 数据接收耗时),建议设为 0(不限制)或足够大值 - Apache 下还要确认
LimitRequestBody(单位字节),Nginx 下对应client_max_body_size,这些都可能早于 PHP 层拦截上传
安全边界:不要信任任何客户端传来的文件信息
$_FILES['file']['type'] 是浏览器提交的 MIME 类型,完全可伪造,不能用于校验。PHP 8.0 的 finfo_open(FILEINFO_MIME_TYPE) 才可靠。
- 用
$finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $_FILES['file']['tmp_name']);获取真实类型 - 对比白名单:
if (!in_array($mime, ['image/jpeg', 'application/pdf'])) { die('Invalid type'); } - 下载时也别暴露真实路径:用 ID 查数据库获取文件物理路径,再校验用户是否有权下载该 ID 对应的资源(防越权)
- 上传目录禁止执行 PHP:
deny from all(Apache)或location ~ \.php$ { return 403; }(Nginx)
最易被忽略的是:上传后没删临时文件($_FILES 的 tmp_name 不会自动清理),而下载时没加 ob_end_clean() 清除可能存在的输出缓冲,导致文件头错乱。



















