PHP读取大文件防止内存溢出,SplFileObject、fopen、fseek三种实现
使用PHP实时读取日志,文件大概500M左右,大小不固定。如采用正常读取,会直接内存溢出。可采用fopen的逐行读取、分块读取、以及Php的SPL类库
- 第一种:使用fopen逐行读取(测试在日志文件较大耗时时间会比较长,如果只是读取一次,可直接使用读取。 如果对日志实时计算处理,可采用SPL类库结合fopen来实现)
- fseek分块读取此处就不提供示例代码,可自行查找。
/**
* 读取文件数据
* @return array
*/
public function getLog()
{
//当前LOG路径
$filename = '文件路径';
$data = [];
//读取数据 防止内存溢出PHP报错,采用文件指针逐行读取
$fh = fopen($filename, 'r');
//声明初始变量
$result = [];
while (! feof($fh)) {
$line = fgets($fh);
var_dump($line);
}
fclose($fh);
}
- 第二种:针对需要使用PHP实时读取LOG并进行业务处理的需求(采用SplFileObject + fopen)
思路为:增加行数冗余,每次只处理新产生的数据,不对全部数据进行处理
每天第一次执行,获取当前文件全部数据,并记录当前执行的行数作为冗余,第二次之后根据第一次冗余的行数,使用SplFileObject的seek转到最后执行位置,只处理新产生LOG记录。
/**
* 返回当前文件行数
* @return string
*/
public function countLine($file){
$fp=fopen($file, "r");
$lineNum=0;
while(!feof($fp)) {
//每次读取2M , 此处可根据实际需求进行调整每次读取文件大小
if($data=fread($fp,1024*1024*2)){
//计算读取到的行数
$num=substr_count($data,"\n");
$lineNum+=$num;
}
}
fclose($fp);
return $lineNum;
}
/**
* 获取Log文件从M 到 N 行数据
* @return array
*/
public function getFileLines($filename, $startLine = 1, $endLine = 50, $method = 'rb'){
$content = [];
$count = $endLine - $startLine;
$fp = new \SplFileObject($filename, $method);
// 转到第N行, seek方法参数从0开始计数
$fp->seek($startLine - 1);
for ($i = 0; $i <= $count; ++$i) {
//获取当前行内容
$content[] = $fp->current();
$fp->next(); // 下一行
}
//过滤之后返回
return array_filter($content);
}