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);
	}