28

fopen当我尝试在PHP. A 6 meg file让它窒息,尽管周围较小的文件100k就可以了。我读过有时需要PHP使用-D_FILE_OFFSET_BITS=64标志重新编译才能读取超过 20 gig 的文件或一些荒谬的文件,但我不应该对 6 meg 文件没有问题吗?最终,我们将要读取大约 100 兆的文件,如果能够打开它们然后使用 fgets 逐行读取它们会很好,因为我可以处理较小的文件。

你有什么技巧/解决方案来读取和执行非常大的文件的操作PHP

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

$rawfile = "mediumfile.csv";

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

另一个更新:感谢大家的帮助,事实证明这确实是一件非常愚蠢的事情——权限问题。当较大的文件没有时,我的小文件莫名其妙地具有读取权限。嗬!

4

8 回答 8

55

您确定这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是否相对于脚本运行的位置?也许尝试在此处为文件名设置绝对路径。

于 2008-10-02T13:29:17.193 回答
11

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

1.3GB

使用fopen()

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

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

使用file()

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

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

9.5 GB

使用fopen()

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

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

使用file()

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

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

似乎file()更快。

于 2015-10-07T20:17:25.143 回答
6

fgets()直到文本文件超过 20 MBytes 并且解析速度大大降低之前,该功能都很好。

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

• 该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 (or get remaining chunk)
      if ( !($last_lf_pos = strrpos($chunk, "\n")) ) $last_lf_pos = mb_strlen($chunk);

      // $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()函数。

于 2019-02-15T07:45:20.673 回答
1

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

如果不是这样——也许你应该考虑一下应用程序的设计,为什么要在 Web 请求上打开这么大的文件?

于 2008-10-02T13:16:57.707 回答
1

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

于 2008-10-02T13:21:46.617 回答
0

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

这将内存限制设置为 12 Mb

ini\_set("memory_limit","12M");
于 2008-10-02T14:13:05.947 回答
0

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

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

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

于 2014-08-04T16:34:36.460 回答
-1

你试过 file() 吗?

http://is2.php.net/manual/en/function.file.php

或 file_get_contents()

http://is2.php.net/manual/en/function.file-get-contents.php

于 2008-10-02T13:28:44.387 回答