PHP fopen()函数
fopen()函数用于打开文件或URL,返回一个文件指针资源,用于后续的文件读写操作。
语法
resource|false fopen ( string $filename , string $mode [, bool $use_include_path = false [, resource $context ]] )
参数说明
| 参数 |
描述 |
filename |
要打开的文件名或URL |
mode |
打开模式(详见下表) |
use_include_path |
可选,如果设置为true,也在include_path中查找文件 |
context |
可选,流上下文资源,可用于设置高级选项 |
打开模式详解
| 模式 |
描述 |
文件存在 |
文件不存在 |
文件指针位置 |
'r' |
只读方式打开,将文件指针指向文件开头 |
成功打开 |
返回false |
文件开头 |
'r+' |
读写方式打开,将文件指针指向文件开头 |
成功打开 |
返回false |
文件开头 |
'w' |
写入方式打开,将文件指针指向文件开头并将文件大小截为零。如果文件不存在则尝试创建 |
清空内容 |
尝试创建 |
文件开头 |
'w+' |
读写方式打开,将文件指针指向文件开头并将文件大小截为零。如果文件不存在则尝试创建 |
清空内容 |
尝试创建 |
文件开头 |
'a' |
写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建 |
成功打开 |
尝试创建 |
文件末尾 |
'a+' |
读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建 |
成功打开 |
尝试创建 |
文件末尾 |
'x' |
创建并以写入方式打开,将文件指针指向文件开头。如果文件已存在则返回false |
返回false |
创建并打开 |
文件开头 |
'x+' |
创建并以读写方式打开,将文件指针指向文件开头。如果文件已存在则返回false |
返回false |
创建并打开 |
文件开头 |
'c' |
写入方式打开,如果文件不存在则创建。文件指针指向文件开头(不会清空文件) |
成功打开 |
创建并打开 |
文件开头 |
'c+' |
读写方式打开,如果文件不存在则创建。文件指针指向文件开头(不会清空文件) |
成功打开 |
创建并打开 |
文件开头 |
示例代码
示例1:基本的文件读写操作
<?php
// 1. 以只读方式打开文件
$file = fopen("example.txt", "r");
if ($file) {
echo "文件打开成功(只读模式)<br>";
// 读取文件内容
$content = fread($file, filesize("example.txt"));
echo "文件内容: " . htmlspecialchars($content) . "<br>";
fclose($file);
} else {
echo "无法打开文件<br>";
}
// 2. 以写入方式打开文件(清空内容)
$file = fopen("output.txt", "w");
if ($file) {
fwrite($file, "这是新写入的内容\n");
fwrite($file, "第二行内容\n");
fclose($file);
echo "文件写入成功(清空模式)<br>";
}
// 3. 以追加方式打开文件
$file = fopen("log.txt", "a");
if ($file) {
fwrite($file, "[" . date('Y-m-d H:i:s') . "] 日志条目\n");
fclose($file);
echo "日志追加成功<br>";
}
?>
示例2:读写模式操作
<?php
// 创建测试文件
file_put_contents("test.txt", "初始内容\n第二行\n第三行");
// 以读写模式打开
$file = fopen("test.txt", "r+");
if ($file) {
echo "=== 初始内容 ===<br>";
echo fread($file, filesize("test.txt"));
// 移动指针到文件开头
rewind($file);
// 覆盖部分内容
fwrite($file, "修改后的内容");
// 移动指针到文件末尾
fseek($file, 0, SEEK_END);
// 追加内容
fwrite($file, "\n追加的内容");
// 回到开头读取
rewind($file);
echo "<br>=== 修改后的内容 ===<br>";
echo fread($file, filesize("test.txt"));
fclose($file);
}
?>
示例3:逐行读取文件
<?php
// 创建包含多行的测试文件
$lines = [
"第一行: PHP是强大的服务器端脚本语言",
"第二行: fopen()用于打开文件",
"第三行: fgets()用于逐行读取",
"第四行: fclose()用于关闭文件"
];
file_put_contents("lines.txt", implode("\n", $lines));
// 逐行读取文件
$file = fopen("lines.txt", "r");
if ($file) {
$lineNumber = 1;
while (!feof($file)) {
$line = fgets($file);
if ($line !== false) {
echo "第{$lineNumber}行: " . htmlspecialchars(trim($line)) . "<br>";
$lineNumber++;
}
}
fclose($file);
}
echo "<br>=== 使用file()函数读取 ===<br>";
// 更简单的方法:使用file()函数
$lines = file("lines.txt", FILE_IGNORE_NEW_LINES);
foreach ($lines as $index => $line) {
echo "行 " . ($index + 1) . ": " . htmlspecialchars($line) . "<br>";
}
?>
示例4:使用上下文选项
<?php
// 1. 创建带HTTP头部的流上下文
$opts = [
'http' => [
'method' => "GET",
'header' => "Accept-language: en\r\n" .
"User-Agent: MyPHPApp/1.0\r\n"
]
];
$context = stream_context_create($opts);
// 打开远程文件
$file = fopen('http://www.example.com/', 'r', false, $context);
if ($file) {
echo "远程文件打开成功<br>";
// 读取前500个字符
$content = fread($file, 500);
echo "内容预览: " . htmlspecialchars(substr($content, 0, 100)) . "...<br>";
fclose($file);
}
// 2. 创建带超时设置的流上下文
$opts = [
'http' => [
'timeout' => 10, // 10秒超时
],
'ssl' => [
'verify_peer' => false,
'verify_peer_name' => false,
]
];
$context = stream_context_create($opts);
// 尝试打开带超时的URL
$url = 'https://jsonplaceholder.typicode.com/posts/1';
$file = @fopen($url, 'r', false, $context);
if ($file) {
$json = stream_get_contents($file);
$data = json_decode($json, true);
echo "<br>获取的JSON数据:<br>";
echo "标题: " . htmlspecialchars($data['title']) . "<br>";
echo "内容: " . htmlspecialchars(substr($data['body'], 0, 50)) . "...<br>";
fclose($file);
} else {
echo "<br>无法打开URL或请求超时<br>";
}
?>
示例5:安全的文件操作类
<?php
class SafeFileHandler {
private $handle = null;
private $filename;
private $mode;
/**
* 打开文件
*/
public function open($filename, $mode = 'r') {
$this->filename = $filename;
$this->mode = $mode;
// 安全检查
if (!$this->validateMode($mode)) {
throw new Exception("无效的文件打开模式: $mode");
}
if (!$this->validateFilename($filename)) {
throw new Exception("无效的文件名: $filename");
}
$this->handle = fopen($filename, $mode);
if (!$this->handle) {
throw new Exception("无法打开文件: $filename");
}
return $this;
}
/**
* 读取文件内容
*/
public function read($length = null) {
if (!$this->handle) {
throw new Exception("文件未打开");
}
if ($length === null) {
return stream_get_contents($this->handle);
}
return fread($this->handle, $length);
}
/**
* 写入内容
*/
public function write($content) {
if (!$this->handle) {
throw new Exception("文件未打开");
}
return fwrite($this->handle, $content);
}
/**
* 关闭文件
*/
public function close() {
if ($this->handle) {
fclose($this->handle);
$this->handle = null;
}
return $this;
}
/**
* 验证打开模式
*/
private function validateMode($mode) {
$validModes = ['r', 'r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'];
return in_array($mode, $validModes);
}
/**
* 验证文件名安全性
*/
private function validateFilename($filename) {
// 防止目录遍历攻击
if (strpos($filename, '../') !== false || strpos($filename, '..\\') !== false) {
return false;
}
// 防止空字节攻击
if (strpos($filename, "\0") !== false) {
return false;
}
// 限制文件路径长度
if (strlen($filename) > 255) {
return false;
}
return true;
}
/**
* 获取文件信息
*/
public function getInfo() {
if (!file_exists($this->filename)) {
return "文件不存在";
}
return [
'filename' => $this->filename,
'size' => filesize($this->filename),
'modified' => date('Y-m-d H:i:s', filemtime($this->filename)),
'mode' => $this->mode
];
}
public function __destruct() {
$this->close();
}
}
// 使用示例
try {
// 1. 创建并写入文件
$handler = new SafeFileHandler();
$handler->open('data.txt', 'w');
$handler->write("Hello, World!\n");
$handler->write("This is a test.\n");
$handler->close();
echo "文件创建成功<br>";
// 2. 读取文件
$handler->open('data.txt', 'r');
$content = $handler->read();
echo "文件内容:<br>" . nl2br(htmlspecialchars($content));
$handler->close();
// 3. 获取文件信息
$info = $handler->getInfo();
echo "<br><br>文件信息:<br>";
echo "文件名: " . $info['filename'] . "<br>";
echo "大小: " . $info['size'] . " 字节<br>";
echo "修改时间: " . $info['modified'] . "<br>";
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
注意事项
重要提示:
- 文件权限:确保PHP进程有足够的权限读写文件
- 路径安全:避免用户输入直接作为fopen()参数,防止目录遍历攻击
- 资源释放:始终使用fclose()关闭打开的文件,避免资源泄漏
- 错误处理:fopen()失败时返回false,应检查返回值
- 并发访问:多进程同时写入文件时,考虑使用flock()进行文件锁定
- 大文件处理:处理大文件时,使用逐块读取而非一次性读取整个文件
- 二进制文件:处理二进制文件(如图片)时,在模式后加'b'(如'rb'、'wb+')
- 远程文件:allow_url_fopen配置必须开启才能打开HTTP/HTTPS URL
示例6:二进制文件操作
<?php
// 1. 创建二进制文件
$binaryData = pack('C*', 0x48, 0x65, 0x6C, 0x6C, 0x6F); // "Hello"的二进制表示
$file = fopen('binary.bin', 'wb');
fwrite($file, $binaryData);
fclose($file);
echo "二进制文件创建成功<br>";
// 2. 读取二进制文件
$file = fopen('binary.bin', 'rb');
$data = fread($file, filesize('binary.bin'));
fclose($file);
echo "二进制数据: ";
for ($i = 0; $i < strlen($data); $i++) {
echo sprintf('%02X ', ord($data[$i]));
}
echo "<br>";
echo "转换为字符串: " . $data . "<br>";
// 3. 复制图片文件(二进制模式)
$source = 'source.jpg';
$dest = 'copy.jpg';
if (file_exists($source)) {
$srcFile = fopen($source, 'rb');
$dstFile = fopen($dest, 'wb');
// 逐块复制(适合大文件)
while (!feof($srcFile)) {
$buffer = fread($srcFile, 4096); // 4KB块
fwrite($dstFile, $buffer);
}
fclose($srcFile);
fclose($dstFile);
echo "图片复制完成<br>";
}
?>
相关函数
fclose() - 关闭一个已打开的文件指针
fwrite() - 写入文件
fread() - 读取文件
fgets() - 从文件指针中读取一行
feof() - 测试文件指针是否到达文件末尾
fseek() - 在文件指针中定位
ftell() - 返回文件指针读/写的位置
rewind() - 倒回文件指针的位置
file_get_contents() - 将整个文件读入字符串
file_put_contents() - 将字符串写入文件
典型应用场景
- 日志记录:使用追加模式('a')记录应用程序日志
- 配置文件读写:读取和修改配置文件
- 数据导入导出:处理CSV、JSON等数据文件
- 缓存文件:生成和读取缓存文件
- 文件上传处理:处理用户上传的文件
- 远程内容获取:通过HTTP/HTTPS协议获取远程资源
- 二进制文件处理:处理图片、PDF等二进制文件