PHP 入门笔记:文件操作与文件上传

· PHP笔记 html 小技巧 PHP 入门笔记 · 0 阅读 · 约 7 分钟

PHP 文件操作基础

打开与关闭文件

使用 fopen 打开文件,第一个参数是文件路径,第二个参数是打开模式,返回一个资源句柄,操作完成后用 fclose 关闭。

$fp = fopen('test.txt', 'r');
// ... 读写操作
fclose($fp);

常用打开模式:

模式 说明
r 只读,指针在文件开头
r+ 读写,指针在文件开头
w 只写,清空文件,不存在则创建
w+ 读写,清空文件
a 追加写入,指针在文件末尾
a+ 追加读写

读取文件

// 读取一行
$line = fgets($fp);
// 读取一个字符
$char = fgetc($fp);
// 读取指定长度
$content = fread($fp, 1024);
// 一次读取整个文件(不需要手动 fopen)
$content = file_get_contents('test.txt');
// 把整个文件读入数组,每行一个元素
$lines = file('test.txt');

写入文件

$fp = fopen('test.txt', 'a');
fwrite($fp, "新的一行内容\n");
fclose($fp);
// 简便写法
file_put_contents('test.txt', "内容\n", FILE_APPEND);

其他常用文件函数

file_exists('test.txt');   // 判断文件是否存在
filesize('test.txt');      // 文件大小(字节)
unlink('test.txt');        // 删除文件
rename('a.txt', 'b.txt');  // 重命名或移动
copy('a.txt', 'b.txt');    // 复制文件
is_file('test.txt');       // 是否为常规文件
is_dir('/var/www');        // 是否为目录
mkdir('newdir');           // 创建目录
rmdir('newdir');           // 删除空目录

文件上传

前端表单

上传文件必须满足两个条件:method 为 post,并且 enctype 设置为 multipart/form-data。

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="photo">
    <input type="submit" value="上传">
</form>

后端处理

上传的文件信息保存在超全局数组 $_FILES 中,以表单里 name 作为键。

var_dump($_FILES['photo']);

$_FILES['photo'] 包含以下字段:

  • name:原始文件名
  • type:MIME 类型(客户端提供,不可信)
  • tmp_name:服务器上的临时文件路径
  • error:错误码
  • size:文件大小(字节)

临时文件在脚本结束后会被自动删除,所以必须用 move_uploaded_file 把它移到目标位置。

if ($_FILES['photo']['error'] !== UPLOAD_ERR_OK) {
    die('上传出错,错误码:' . $_FILES['photo']['error']);
}

// 简单校验:只允许 jpg 和 png
$allow = ['image/jpeg', 'image/png'];
if (!in_array($_FILES['photo']['type'], $allow, true)) {
    die('文件类型不允许');
}

// 按日期分目录
$dir = './uploads/' . date('Ymd') . '/';
if (!is_dir($dir)) {
    mkdir($dir, 0755, true);
}

// 生成随机文件名,避免中文名和重名问题
$ext = pathinfo($_FILES['photo']['name'], PATHINFO_EXTENSION);
$newName = md5(uniqid(mt_rand(), true)) . '.' . $ext;

if (move_uploaded_file($_FILES['photo']['tmp_name'], $dir . $newName)) {
    echo '上传成功:' . $dir . $newName;
} else {
    echo '移动文件失败';
}

错误码说明

错误码 含义
UPLOAD_ERR_OK (0) 上传成功
UPLOAD_ERR_INI_SIZE (1) 超过 php.ini 中 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) 找不到临时目录
UPLOAD_ERR_CANT_WRITE (7) 文件写入失败

相关配置项

在 php.ini 中可以调整上传相关的配置:

file_uploads = On
upload_max_filesize = 2M
post_max_size = 8M
max_file_uploads = 20

注意 post_max_size 要大于等于 upload_max_filesize,否则整个 POST 数据都会被丢弃,$_POST 和 $_FILES 都会是空的。

安全校验要点

客户端传来的 type 和 name 都可以伪造,实际项目中应该:

  1. 使用 finfo_file 检测真实 MIME 类型
  2. 用 getimagesize 验证是否真的是图片(针对图片上传)
  3. 自己生成随机文件名,禁止直接使用用户上传的文件名
  4. 把上传目录设置为不可执行脚本,例如 Nginx 中禁用该目录的 PHP 解析
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['photo']['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, ['image/jpeg', 'image/png'], true)) {
    die('不是合法的图片文件');
}

小结

文件操作核心是 fopen 系列函数和 file_get_contents、file_put_contents 这两个简便函数;文件上传核心是理解 $_FILES 的结构,用 move_uploaded_file 保存临时文件,同时做好类型校验和文件名处理,注意 php.ini 中上传大小限制的配置。