注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

前端开发那点事儿

冒犯之处,敬请谅解。

 
 
 

日志

 
 
 
 

PHP对大文件的处理思路  

2013-03-06 11:20:37|  分类: 默认分类 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
FROM: http://www.dedecms.com/knowledge/program/php/2013/0227/22618.html
需求: 现有一个1G左右的日志文件,大约有500多万行, 用 php 返回最后几行的内容。 在php中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file、file_get_contents之类 的函数,简简单单的几行代码就能很漂亮的完成我们所需要的功能。但当所操作的文
  • 需求: 现有一个1G左右的日志文件,大约有500多万行, 用php返回最后几行的内容。
    在php中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file、file_get_contents之类 的函数,简简单单的几行代码就能很漂亮的完成我们所需要的功能。但当所操作的文件是一个比较大的文件时,这些函数可能就显的力不从心, 下面将从一个需求入手来说明对于读取大文件时,常用的操作方法。
    1. 直接采用file函数来操作
    由于 file函数是一次性将所有内容读入内存,而php为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下 限制只能最大使用内存16M,这是通过php.ini里的memory_limit = 16M来进行设置,这个值如果设置-1,则内存使用量不受限制。
    下面是一段用file来取出这具文件最后一行的代码。代码执行大概2分钟左右。
     
    01 $fp = fopen($file, "r"); 
    02 $num = 10; 
    03 $chunk = 4096; 
    04 $fs = sprintf("%u", filesize($file)); 
    05 $max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file); 
    06 for ($len = 0; $len < $max; $len += $chunk) { 
    07   $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len; 
    08     fseek($fp, ($len + $seekSize) * -1, SEEK_END); 
    09     $readData = fread($fp, $seekSize) . $readData; 
    10   
    11     if (substr_count($readData, "\n") >= $num + 1) { 
    12         preg_match("!(.*?\n){".($num)."}$!", $readData, $match); 
    13         $data = $match[0]; 
    14         break; 
    15     } 
    16 } 
    17 fclose($fp); 
    18 echo $data; 
    我机器是2个G的内存,当按下F5运行时,系统直接变灰,差不多20分钟后才恢复过来,可见将这么大的文件全部直接读入内存,后果是多少严重,所以不在万不得以,memory_limit这东西不能调得太高,否则只有打电话给机房,让reset机器了。
    2.直接调用linux的tail命令来显示最后几行
    在linux命令行下,可以直接使用tail -n 10 access.log很轻易的显示日志文件最后几行,可以直接用php来调用tail命令,执行php代码如下.整个代码执行完成耗时 0.0034 (s)
     
    1 file = 'access.log'; 
    2 $file = escapeshellarg($file); // 对命令行参数进行安全转义 
    3 $line = `tail -n 1 $file`; 
    4 echo $line; 
    3. 直接使用php的fseek来进行文件操作
    这种方式是最为普遍的方式,它不需要将文件的内容全部读入内存,而是直接通过指针来操作,所以效率是相当高效的.在使用fseek来对文件进行操作时,也有多种不同的方法,效率可能也是略有差别的,下面是常用的两种方法.
    方法一:
    首先通过fseek找到文件的最后一位EOF,然后找最后一行的起始位置,取这一行的数据,再找次一行的起始位置,再取这一行的位置,依次类推,直到找到了$num行。
    view sourceprint?
    01 function tail($fp,$n,$base=5) 
    02 { 
    03     assert($n>0); 
    04     $pos = $n+1; 
    05     $lines = array(); 
    06     while(count($lines)< =$n){ 
    07         try{ 
    08             fseek($fp,-$pos,SEEK_END); 
    09         } catch (Exception $e){ 
    10             fseek(0); 
    11             break; 
    12         } 
    13         $pos *= $base; 
    14         while(!feof($fp)){ 
    15             array_unshift($lines,fgets($fp)); 
    16         } 
    17     } 
    18     return array_slice($lines,0,$n); 
    19 } 
    20 var_dump(tail(fopen("access.log","r+"),10)); 
    方法二:
    还是采用fseek的方式从文件最后开始读,但这时不是一位一位的读,而是一块一块的读,每读一块数据时,就将读取后的数据放在一个buf里,然后通过换行符(\n)的个数来判断是否已经读完最后$num行数据.
     
    01 $fp = fopen($file, "r"); 
    02 $line = 10; 
    03 $pos = -2; 
    04 $t = " "; 
    05 $data = ""; 
    06 while ($line > 0) { 
    07     while ($t != "\n") { 
    08         fseek($fp, $pos, SEEK_END); 
    09         $t = fgetc($fp); 
    10         $pos --; 
    11     } 
    12     $t = " "; 
    13     $data .= fgets($fp); 
    14     $line --; 
    15 } 
    16 fclose ($fp); 
    17 echo $data 
    方法三:
     
    1 ini_set('memory_limit','-1'); 
    2 $file = 'access.log'; 
    3 $data = file($file); 
    4 $line = $data[count($data)-1]; 
    5 echo $line;



——————

示例:
<?php
    include_once('global.php');
    include_once('verifysession.php');
   
    $db = GetDBConn($gDBConfig);
    $file = new File($db);
   
    $row = $file->getDirect($_GET['id']);
    $direct = $row['direct'];
//    $filename = iconv('utf-8','gb2312',$row['filename']);
    $filename = $row['filename'];
   
    switch($direct){
        case '0':
            $path = './clientfile/' . $filename;
            break;
        case '1':
            $path = './vsfile/' . $filename;
            break;
        case '2':
            $path = './vsfile/' . $filename;
            break;
        case '3':
            $path = './vsfile/' . $filename;
            break;
        case '4':
            $path = './vsfile/' . $filename;
            break;
        case '5':
            $path = './serverfile/' . $filename;
            break;
    }
   
    if(!file_exists($path)){
        echo $path . "--404 file not Found! ";
        exit;
    }else{
        set_time_limit(0);
        $file_size = filesize($path);
        header("Content-type:application/octet-stream");
        header("Accept-Ranges:bytes");
        Header("Accept-Length: " . $file_size);
        header("Content-Disposition: attachment; filename=\"" . $filename . "\";");
        if($file_size <= 1024){
            @readfile($path);
        }else{
            $fp=fopen($path,"r");
            $buffer_size=1024;
            $cur_pos=0;
            while(!feof($fp) && ($file_size - $cur_pos) > $buffer_size)
            {
                echo fread($fp,$buffer_size);
                $cur_pos += $buffer_size;
            }
            echo fread($fp,$file_size-$cur_pos);
        }
       
        fclose($fp);
   }
?>
  评论这张
 
阅读(656)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017