readfile() 函数用于读取文件并将其写入输出缓冲区。该函数返回从文件中读取的字节数。
与 file_get_contents() 不同,readfile() 不会将整个文件内容读入内存,而是分块读取并直接输出,这使得它非常适合处理大文件。
readfile ( string $filename , bool $use_include_path = false , resource $context = null ) : int|false
| 参数 | 类型 | 说明 |
|---|---|---|
filename |
字符串 | 要读取的文件名 |
use_include_path |
布尔值 | 可选。如果设置为true,会在包含路径(include_path)中搜索文件。默认为false |
context |
资源 | 可选。文件句柄的上下文资源。可以通过 stream_context_create() 创建 |
成功时返回从文件中读取的字节数,失败时返回 false。
<?php
$filename = 'example.txt';
// 创建一个示例文件
file_put_contents($filename, "这是第一行\n这是第二行\n这是第三行");
// 读取并输出文件内容
$bytes = readfile($filename);
if ($bytes !== false) {
echo "\n\n成功读取了 {$bytes} 字节的数据";
} else {
echo "读取文件失败";
}
// 清理
unlink($filename);
?>
<?php
$imageFile = 'image.jpg';
// 检查文件是否存在
if (file_exists($imageFile)) {
// 设置正确的 Content-Type
header('Content-Type: image/jpeg');
// 输出图片
$bytes = readfile($imageFile);
if ($bytes === false) {
// 如果readfile失败,发送404图片
header("HTTP/1.0 404 Not Found");
echo "图片文件无法读取";
}
} else {
header("HTTP/1.0 404 Not Found");
echo "图片文件不存在";
}
?>
<?php
// 文件下载函数
function downloadFile($filePath, $displayName = null) {
// 检查文件是否存在
if (!file_exists($filePath) || !is_readable($filePath)) {
header("HTTP/1.0 404 Not Found");
return false;
}
// 获取文件名
$filename = $displayName ?? basename($filePath);
// 获取文件大小
$filesize = filesize($filePath);
// 设置HTTP头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $filesize);
// 清空输出缓冲区
if (ob_get_level()) {
ob_end_clean();
}
// 读取并输出文件
$bytes = readfile($filePath);
// 返回读取的字节数
return $bytes;
}
// 使用示例
$file = 'document.pdf';
$result = downloadFile($file);
if ($result !== false) {
// 下载成功,记录日志等
error_log("文件 {$file} 被下载,大小: {$result} 字节");
} else {
echo "文件下载失败";
}
?>
<?php
// 对于非常大的文件,可以手动分块读取以提高性能
function readLargeFile($filename, $chunkSize = 8192) {
if (!file_exists($filename) || !is_readable($filename)) {
return false;
}
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
$totalBytes = 0;
// 分块读取并输出
while (!feof($handle)) {
$buffer = fread($handle, $chunkSize);
echo $buffer;
$totalBytes += strlen($buffer);
}
fclose($handle);
return $totalBytes;
}
// 使用readfile的替代方法
$filename = 'large_video.mp4';
// 设置视频内容类型
header('Content-Type: video/mp4');
header('Content-Length: ' . filesize($filename));
// 方法1:使用readfile(PHP内部会分块处理)
// $bytes = readfile($filename);
// 方法2:使用自定义分块函数
$bytes = readLargeFile($filename, 65536); // 64KB块
if ($bytes !== false) {
// 记录成功
error_log("成功输出 {$bytes} 字节的大文件");
}
?>
<?php
// 创建HTTP上下文,设置超时
$contextOptions = [
'http' => [
'timeout' => 10, // 10秒超时
'header' => "User-Agent: MyPHPApp/1.0\r\n"
]
];
$context = stream_context_create($contextOptions);
$files = [
'local_file.txt',
'nonexistent.txt',
'https://example.com/robots.txt' // 远程文件
];
foreach ($files as $file) {
echo "尝试读取文件: $file\n";
// 使用@抑制警告,手动处理错误
$bytes = @readfile($file, false, $context);
if ($bytes === false) {
$error = error_get_last();
echo "读取失败: " . ($error['message'] ?? '未知错误') . "\n";
} else {
echo "成功读取 {$bytes} 字节\n";
}
echo str_repeat('-', 50) . "\n";
}
?>
| 函数 | 内存使用 | 返回值 | 适用场景 |
|---|---|---|---|
readfile() |
低(分块) | 字节数/false | 文件下载、输出静态资源、大文件处理 |
file_get_contents() |
高(全部) | 文件内容/false | 读取配置文件、小文件处理、需要操作内容时 |
fread() |
可控 | 字符串/false | 需要精细控制读取过程的场景 |
file() |
高(全部) | 数组/false | 需要按行处理文件内容时 |