【问题标题】:PHP: fseek() for large file (>2GB)PHP: fseek() 用于大文件 (>2GB)
【发布时间】:2013-06-11 11:27:06
【问题描述】:

我有一个非常大的文件(大约 20GB),如何使用 fseek() 来跳转并读取其内容。

代码如下所示:

function read_bytes($f, $offset, $length) {
    fseek($f, $offset);
    return fread($f, $length);
}

只有当 $offset

更新:我在 Windows 64 上运行, phpinfo - 架构:x64, PHP_INT_MAX:2147483647

【问题讨论】:

  • 如何打开文件?
  • $f = fopen('data.log', 'r');
  • 您使用的是 32 位还是 64 位?
  • 如果你正在运行x64,你的PHP_INT_MAX应该是9223372036854775807

标签: php


【解决方案1】:

警告: 如 cmets 所述,fseek 在内部使用 INT,它根本无法工作 在 32 位 PHP 编译中使用如此大的文件。以下解决方案 不会工作。留在这里仅供参考。

一点点搜索让我找到了 fseek 的 PHP 手册页上的 cmets:

http://php.net/manual/en/function.fseek.php

问题是偏移参数的最大 int 大小,但似乎您可以通过使用 SEEK_CUR 选项进行多次 fseek 调用并将其与大数字处理库之一混合来解决它。

示例:

function fseek64(&$fh, $offset)
{
    fseek($fh, 0, SEEK_SET);
    $t_offset   = '' . PHP_INT_MAX;
    while (gmp_cmp($offset, $t_offset) == 1)
    {
        $offset     = gmp_sub($offset, $t_offset);
        fseek($fh, gmp_intval($t_offset), SEEK_CUR);
    }
    return fseek($fh, gmp_intval($offset), SEEK_CUR);
}

fseek64($f, '23456781232');

【讨论】:

  • 嗯,但是如果你在 64 位系统上使用 64 位 PHP 可能问题出在其他地方。
  • 这在 php 上的 cmets 中也被否决了,我认为这可能是因为 $offset = $offset - $t_offset; 这一行必须将 $offset 转换为 int 以解决分配的右侧不能高于 PHP_INT_MAX
  • 我尝试了 20GB 文件:fseek64($f, 2200000000); // 2,200,000,000echo fread($f, 100); 然后我尝试了该文件的一小部分,20 亿字节,第二部分:fseek($f, 200000000); // 200,000,000echo fread($f, 100); 两个结果不同。跨度>
  • 我今天试过了,在 32 位系统上不起作用,那是因为 fseek 内部使用 INT 来存储当前文件指针,而 INT 不能超过 2G。
  • 感谢您对此进行测试。所以就像我很担心一样,添加了这个来回答。
【解决方案2】:

对于我的项目,我需要从 BIG 文件 (>3 GB) 中的 BIG 偏移读取 10KB 的块。写入始终是附加的,因此不需要偏移量。

无论您使用的是哪个 PHP 版本和操作系统,这都会起作用。

先决条件 = 您的服务器应支持范围检索查询。 Apache 和 IIS 已经支持这一点,99% 的其他网络服务器(共享主机或其他)也支持这一点

// offset, 3GB+
$start=floatval(3355902253);

// bytes to read, 100 KB
$len=floatval(100*1024);

// set up the http byte range headers
$opts = array('http'=>array('method'=>'GET','header'=>"Range: bytes=$start-".($start+$len-1)));
$context = stream_context_create($opts);
// bytes ranges header
print_r($opts);

// change the URL below to the URL of your file. DO NOT change it to a file path.
// you MUST use a http:// URL for your file for a http request to work
// this will output the results
echo $result = file_get_contents('http://127.0.0.1/dir/mydbfile.dat', false, $context);

// status of your request
// if this is empty, means http request didnt fire. 
print_r($http_response_header);

// Check your file URL and verify by going directly to your file URL from a web 
// browser. If http response shows errors i.e. code > 400 check you are sending the
// correct Range headers bytes. For eg - if you give a start Range which exceeds the
// current file size, it will give 406. 

// NOTE  - The current file size is also returned back in the http response header
// Content-Range: bytes 355902253-355903252/355904253, the last number is the file size

...

...

...

安全 - 您必须添加一个 .htaccess 规则,该规则拒绝对该数据库文件的所有请求,但来自本地 ip 127.0.0.1 的请求除外。

【讨论】:

  • 我尝试了这个解决方案,但内存已经耗尽。使用offset/maxlen parametersRange header 是否有助于防止file_get_contents 将整个文件读入内存?
  • http 范围标头查询仅检索您请求的数量,在本例中为 100KB,因此没有内存问题。所以问题出在php脚本上。如果您阅读 10MB,请确保将 php 内存限制设置为两倍。 ini_set('memory_limit', '20M');
  • 我只读取了 20GB 文件的 200 字节 ($len = floatval(200);)。我的memory_limit 是 1024M(我也尝试了更大的值来测试)。该请求需要几分钟才能将文件加载到内存中,然后因内存耗尽错误而中断。
  • “请求需要几分钟才能加载”这令人震惊。我在我的 3.5 gb 文件上执行此操作,无论我给出的是大偏移量还是小偏移量,它都花了几毫秒。你能发布这个信息吗? print_r($http_response_header);它将显示 http 范围请求的问题所在。
  • 顺便说一句,我在 32 位 windows xp 上,使用 xampp (apache + php 5.3),它与 ​​http 范围请求完美配合。请分享此代码的响应 - print_r($http_response_header);
猜你喜欢
  • 2011-06-04
  • 1970-01-01
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-28
  • 2011-07-02
  • 1970-01-01
相关资源
最近更新 更多