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() - 将字符串写入文件

典型应用场景

  1. 日志记录:使用追加模式('a')记录应用程序日志
  2. 配置文件读写:读取和修改配置文件
  3. 数据导入导出:处理CSV、JSON等数据文件
  4. 缓存文件:生成和读取缓存文件
  5. 文件上传处理:处理用户上传的文件
  6. 远程内容获取:通过HTTP/HTTPS协议获取远程资源
  7. 二进制文件处理:处理图片、PDF等二进制文件