在PHP中读取非常大的文件

问题描述 投票:24回答:8

当我尝试在fopen中读取一个非常适中的文件时,PHP失败了。 A 6 meg file让它窒息,虽然100k附近的小文件就好了。我已经读过,有时需要用PHP标志重新编译-D_FILE_OFFSET_BITS=64,以便读取超过20演出或者荒谬的文件,但是我不应该对6 meg文件没有问题吗?最终我们想要读取大约100兆的文件,并且能够打开它们然后逐行读取它们将很好,因为我可以使用较小的文件。

PHP中读取和操作非常大的文件的技巧/解决方案是什么?

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

$rawfile = "mediumfile.csv";

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

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

php file large-files
8个回答
46
投票

你确定它的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的路径是否正确?也许尝试在这里为文件名设置一个绝对路径。


7
投票

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

1.3 GB

使用fopen()

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

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

使用file()

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

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

9.5 GB

使用fopen()

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

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

使用file()

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

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

似乎file()更快。


1
投票

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

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


1
投票

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


1
投票

fgets()函数很好,直到文本文件超过20 MB,并且解析速度大大降低。

file_ get_contents()函数提供了良好的结果,直到40 MBytes和可接受的结果,直到100 MB,但file_get_contents()将整个文件加载到内存中,所以它不是scalabile。

file()函数对于大文本文件是灾难性的,因为此函数创建包含每行文本的数组,因此该数组存储在内存中,并且使用的内存更大。 实际上,一个200 MB的文件,我只能设法用memory_limit解析,设置为2 GB,这对于我打算解析的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


0
投票

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

这将内存限制设置为12 Mb

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

0
投票

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

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

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


© www.soinside.com 2019 - 2024. All rights reserved.