PHP 入门教程
26.2
3. 用
PHP 文件上传
本教程共 65 篇 · 第 26 篇 · 更新于 2026-07-24 · 约 5 分钟阅读
PHPPHP8文件上传$_FILESMIME安全
26. PHP 文件上传
本节目标:学会通过 HTML 表单上传文件,掌握
$_FILES结构、上传目录配置和安全检查。
用户头像、附件、图片等资源都需要文件上传功能。本章教你安全地实现它。
26.1 上传表单的特殊配置
文件上传表单有两个必须注意的地方:
method必须是postenctype必须是multipart/form-data
<!-- upload.html -->
<!DOCTYPE html>
<html>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="myfile"><br>
<input type="submit" value="上传">
</form>
</body>
</html>
Note忘记写
enctype是最常见的错误。没有它,PHP 接收不到文件内容。
26.2 $_FILES 数组结构
文件上传后,PHP 将信息存入 $_FILES:
<?php
print_r($_FILES["myfile"]);
/*
Array
(
[name] => photo.jpg // 原始文件名
[type] => image/jpeg // MIME 类型(客户端提供,不可信)
[tmp_name] => /tmp/phpXX.tmp // 服务器临时路径
[error] => 0 // 错误码,0 表示成功
[size] => 204800 // 文件大小(字节)
)
*/
错误码对照表
| 错误码 | 含义 |
|---|---|
UPLOAD_ERR_OK (0) | 上传成功 |
UPLOAD_ERR_INI_SIZE (1) | 超出 upload_max_filesize 限制 |
UPLOAD_ERR_FORM_SIZE (2) | 超出表单 MAX_FILE_SIZE 限制 |
UPLOAD_ERR_PARTIAL (3) | 文件只有部分被上传 |
UPLOAD_ERR_NO_FILE (4) | 没有文件被上传 |
UPLOAD_ERR_NO_TMP_DIR (6) | 找不到临时文件夹 |
26.3 基本上传处理
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$file = $_FILES["myfile"] ?? null;
if (!$file || $file["error"] !== UPLOAD_ERR_OK) {
die("上传失败,错误码:" . ($file["error"] ?? "无文件"));
}
$uploadDir = "uploads/";
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// 为防止文件名冲突,重命名为唯一 ID
$ext = pathinfo($file["name"], PATHINFO_EXTENSION);
$newName = uniqid("img_", true) . "." . strtolower($ext);
$destPath = $uploadDir . $newName;
if (move_uploaded_file($file["tmp_name"], $destPath)) {
echo "上传成功!保存为:{$newName}";
} else {
echo "保存文件失败";
}
}
?>
Tip
move_uploaded_file()比copy()更安全,它会检查文件确实是通过 HTTP POST 上传的,防止路径遍历攻击。
26.4 安全检查清单
生产环境必须做以下检查:
1. 限制文件大小
<?php
$maxSize = 2 * 1024 * 1024; // 2MB
if ($file["size"] > $maxSize) {
die("文件过大,不能超过 2MB");
}
2. 限制扩展名
<?php
$allowedExts = ["jpg", "jpeg", "png", "gif", "webp"];
$ext = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExts, true)) {
die("只允许上传图片文件");
}
Note仅用
$_FILES["type"]判断不安全,它是客户端提供的。务必检查扩展名,并最好用finfo验证真实 MIME 类型。
3. 用 finfo 验证真实类型
<?php
$finfo = new finfo(FILEINFO_MIME_TYPE);
$realType = $finfo->file($file["tmp_name"]);
$allowedTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
if (!in_array($realType, $allowedTypes, true)) {
die("文件类型不符合要求");
}
4. 防止覆盖与路径遍历
<?php
// 危险:直接使用用户提供的文件名
// move_uploaded_file($file["tmp_name"], "uploads/" . $file["name"]);
// 安全:生成新文件名
$newName = bin2hex(random_bytes(8)) . "." . $ext;
// PHP 8.0+ 可用 random_bytes,比 uniqid 更安全
26.5 多文件上传
HTML 表单加上 multiple 和数组形式的 name:
<input type="file" name="photos[]" multiple>
PHP 处理时需要循环:
<?php
$files = $_FILES["photos"];
$count = count($files["name"]);
for ($i = 0; $i < $count; $i++) {
if ($files["error"][$i] === UPLOAD_ERR_OK) {
$ext = pathinfo($files["name"][$i], PATHINFO_EXTENSION);
$newName = bin2hex(random_bytes(8)) . "." . strtolower($ext);
move_uploaded_file($files["tmp_name"][$i], "uploads/" . $newName);
echo "已保存:{$newName}<br>";
}
}
Tip多文件上传时
$_FILES结构变成二维数组,注意下标是files["name"][0]而非files[0]["name"]。
26.6 php.ini 相关配置
上传功能受以下配置影响:
| 配置项 | 说明 | 默认值 |
|---|---|---|
file_uploads | 是否允许上传 | On |
upload_max_filesize | 单个文件最大大小 | 2M |
post_max_size | POST 数据总大小 | 8M |
upload_tmp_dir | 临时文件目录 | 系统默认 |
Note
post_max_size必须大于upload_max_filesize,否则大文件会被截断。
来源:参考了 runoob「PHP 文件上传」、w3cschool「PHP 文件上传」等,改写后所得。