在PHP中读取非常大的文件

时间:2008-10-02 13:11:46

标签: php file large-files

当我尝试在fopen中读取一个非常适中的文件时,

PHP失败了。虽然A 6 meg file周围的小文件很好,100k会让它变得窒息。我已经读过有时需要用PHP标志重新编译-D_FILE_OFFSET_BITS=64以便读取超过20场演出的文件或者荒谬的东西,但是对于6兆字节的文件我不应该没有问题吗?最终我们想要读取大约100兆的文件,并且能够打开它们然后逐行读取它们将很好,因为我可以使用较小的文件。

您在PHP中对非常大的文件进行阅读和操作的技巧/解决方案是什么?

更新:这是一个在我的6 meg文件上失败的简单代码块的示例 - PHP似乎没有抛出错误,它只返回false。也许我正在做一些非常愚蠢的事情?

$rawfile = "mediumfile.csv";

if($file = fopen($rawfile, "r")){  
  fclose($file);
} else {
  echo "fail!";
}

另一个更新:感谢所有人的帮助,它确实是令人难以置信的愚蠢 - 权限问题。当较大的文件没有时,我的小文件莫名其妙地具有读权限。卫生署!

8 个答案:

答案 0 :(得分:45)

你确定它是fopen失败而不是你的脚本的超时设置吗?默认值通常约为30秒左右,如果您的文件花费的时间超过了读取的时间,则可能会使文件绊倒。

要考虑的另一件事可能是脚本的内存限制 - 将文件读入数组可能会超过此值,因此请检查错误日志中的内存警告。

如果上述问题都不是您的问题,您可以考虑使用fgets逐行读取文件,然后进行处理。

$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgets($handle, 4096);
        // Process buffer here..
    }
    fclose($handle);
}

修改

  

PHP似乎没有抛出错误,它只返回false。

$rawfile的路径相对于脚本运行的位置是否正确?也许尝试在这里为文件名设置一个绝对路径。

答案 1 :(得分:7)

使用1.3GB文件和9.5GB文件进行了2次测试。

1.3 GB

使用fopen()

此过程使用15555毫秒进行计算。

它在系统调用中花费了169毫秒。

使用file()

此过程使用6983 ms进行计算。

它在系统调用中花了4469毫秒。

9.5 GB

使用fopen()

此过程使用113559毫秒进行计算。

它在系统调用中花了2532毫秒。

使用file()

此过程使用8221 ms进行计算。

它在系统调用中花费了7998毫秒。

似乎file()更快。

答案 2 :(得分:1)

如果您只想输出文件,可以尝试使用readfile函数。

如果不是这种情况 - 也许您应该考虑应用程序的设计,为什么要在Web请求上打开如此大的文件?

答案 3 :(得分:1)

我使用fopen打开视频文件进行流式传输,使用php脚本作为视频流服务器,我对大小超过50/60 MB的文件没有任何问题。

答案 4 :(得分:1)

•在文本文件超过20 MB且解析速度大大降低之前,fgets()功能还不错。

file_ get_contents()函数在40 MBytes之前给出了良好的结果,直到100 MBytes才得到可接受的结果,但是 file_get_contents()将整个文件加载到内存中,因此它不是可扩展的。

file()函数对于大文本文件来说是灾难性的,因为此函数创建一个包含每一行文本的数组,因此该数组存储在内存中,并且使用的内存更大。
实际上,我只能用设置为2 GB的memory_limit来解析200 MB的文件,这不适合我打算解析的1 GB以上的文件。

当您必须解析大于1 GB的文件并且解析时间超过15秒并且要避免将整个文件加载到内存中时,必须寻找另一种方法。

我的解决方案是任意小块地解析数据。代码是:

$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;

// if handle $fp to file was created, go ahead
if ($fp) {
   while(!feof($fp)){
      // move pointer to $position in file
      fseek($fp, $position);

      // take a slice of $chunk_size bytes
      $chunk = fread($fp,$chunk_size);

      // searching the end of last full text line
      $last_lf_pos = strrpos($chunk, "\n");

      // $buffer will contain full lines of text
      // starting from $position to $last_lf_pos
      $buffer = mb_substr($chunk,0,$last_lf_pos);

      ////////////////////////////////////////////////////
      //// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
      ////////////////////////////////////////////////////

      // Move $position
      $position += $last_lf_pos;

      // if remaining is less than $chunk_size, make $chunk_size equal remaining
      if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
      $buffer = NULL;
   }
   fclose($fp);
}

所使用的内存仅为$chunk_size,并且速度略低于使用file_ get_contents()获得的内存。我认为PHP Group应该使用我的方法来优化其解析功能。

*)找到get_file_size()函数here

答案 5 :(得分:0)

如果问题是由达到内存限制引起的,您可以尝试将其设置为更高的值(这可能有效或无效,具体取决于php的配置)。

这将内存限制设置为12 Mb

ini\_set("memory_limit","12M");

答案 6 :(得分:0)

对我来说,fopen()文件超过1mb的速度非常慢,file()要快得多。

只是尝试一次读取100行并创建批量插入,fopen()需要37秒,而file()需要4秒。必须是string->array

中内置的file()步骤

我会尝试所有文件处理选项,看看哪种方法最适合您的应用程序。

答案 7 :(得分:-1)