Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/237.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
PHP-使用fsockopen()、fgets()和feof()下载非常大的文件_Php - Fatal编程技术网

PHP-使用fsockopen()、fgets()和feof()下载非常大的文件

PHP-使用fsockopen()、fgets()和feof()下载非常大的文件,php,Php,我在一个类中有一个简单的下载函数,这个类可能一次处理来自Amazon Web服务bucket的数百兆字节的文件。整个文件不能一次加载到内存中,因此必须直接流式传输到文件指针。这是我的理解,因为这是我第一次处理这个问题,我会在处理的过程中不断总结 我最终得出的结论是,基于4KB的文件缓冲区,简单的测试表明该缓冲区的大小很好: $fs = fsockopen($host, 80, $errno, $errstr, 30); if (!$fs) {

我在一个类中有一个简单的下载函数,这个类可能一次处理来自Amazon Web服务bucket的数百兆字节的文件。整个文件不能一次加载到内存中,因此必须直接流式传输到文件指针。这是我的理解,因为这是我第一次处理这个问题,我会在处理的过程中不断总结

我最终得出的结论是,基于4KB的文件缓冲区,简单的测试表明该缓冲区的大小很好:

        $fs = fsockopen($host, 80, $errno, $errstr, 30);

        if (!$fs) {
          $this->writeDebugInfo("FAILED ", $errstr . '(' . $errno . ')');
        } else {
          $out = "GET $file HTTP/1.1\r\n";
          $out .= "Host: $host\r\n";
          $out .= "Connection: Close\r\n\r\n";
          fwrite($fs, $out);

          $fm = fopen ($temp_file_name, "w");
          stream_set_timeout($fs, 30);

          while(!feof($fs) && ($debug = fgets($fs)) != "\r\n" ); // ignore headers

          while(!feof($fs)) {
            $contents = fgets($fs, 4096); 
            fwrite($fm, $contents);
            $info = stream_get_meta_data($fs);
            if ($info['timed_out']) {
              break;
            }
          }
          fclose($fm);
          fclose($fs);

          if ($info['timed_out']) {
            // Delete temp file if fails
            unlink($temp_file_name);
            $this->writeDebugInfo("FAILED - Connection timed out: ", $temp_file_name);
          } else {
            // Move temp file if succeeds
            $media_file_name = str_replace('temp/', 'media/', $temp_file_name);
            rename($temp_file_name, $media_file_name);
            $this->writeDebugInfo("SUCCESS: ", $media_file_name);
          }
        }
在测试中,这很好。然而,我与某人进行了一次对话,他说我不理解
fgets()
feof()
如何协同工作,他提到分块编码是一种更有效的方法


代码是否正常,或者我是否遗漏了一些重要的内容?分块编码能给我带来什么好处?

我觉得你的解决方案不错,不过我有几点意见

1) 不要自己创建HTTP数据包,即不要发送HTTP请求。取而代之的是使用类似卷曲的东西。这是一种更简单的方法,它将支持服务器可能回复的更广泛的响应。此外,还可以将CURL设置为直接写入文件,这样您就不用自己编写了

2) 如果正在读取二进制数据,则使用FGET可能会出现问题。Fgets读取到一行的末尾,使用二进制数据可能会损坏您的下载。相反,我建议fread(4096美元);它将处理文本和二进制数据

2) 分块编码是Web服务器以多个分块向您发送响应的一种方式。我认为这对您不是很有用,但是,Web服务器可能支持的更好的编码是gzip编码。这将允许Web服务器动态压缩响应。如果您使用像CURL这样的库,它会告诉服务器它支持gzip,然后自动为您解压


我希望这有助于

不要处理套接字,优化代码并使用库。像这样:

$url = 'http://'.$host.'/'.$file;
// create a new cURL resource
$fh = fopen ($temp_file_name, "w");
$ch = curl_init();
// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FILE, $fh); 
//curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// grab URL and pass it to the browser
curl_exec($ch);
// close cURL resource, and free up system resources
curl_close($ch);
fclose($fh);

最后的结果,以防它对其他人有帮助。我还将整个过程包装在重试循环中,以降低下载完全失败的风险,但它确实增加了资源的使用:

      do {
        $fs = fopen('http://' . $host . $file, "rb");

        if (!$fs) {
          $this->writeDebugInfo("FAILED ", $errstr . '(' . $errno . ')');
        } else {
          $fm = fopen ($temp_file_name, "w");
          stream_set_timeout($fs, 30);

          while(!feof($fs)) {
            $contents = fread($fs, 4096); // Buffered download
            fwrite($fm, $contents);
            $info = stream_get_meta_data($fs);
            if ($info['timed_out']) {
              break;
            }
          }
          fclose($fm);
          fclose($fs);

          if ($info['timed_out']) {
            // Delete temp file if fails
            unlink($temp_file_name);
            $this->writeDebugInfo("FAILED on attempt " . $download_attempt . " - Connection timed out: ", $temp_file_name);
            $download_attempt++;
            if ($download_attempt < 5) {
              $this->writeDebugInfo("RETRYING: ", $temp_file_name);
            }
          } else {
            // Move temp file if succeeds
            $media_file_name = str_replace('temp/', 'media/', $temp_file_name);
            rename($temp_file_name, $media_file_name);
            $this->newDownload = true;
            $this->writeDebugInfo("SUCCESS: ", $media_file_name);
          }
        }
      } while ($download_attempt < 5 && $info['timed_out']);
do{
$fs=fopen('http://'.$host.$file,“rb”);
如果(!$fs){
$this->writeDebugInfo(“失败”、$errstr.('.$errno.'));
}否则{
$fm=fopen($temp_文件名,“w”);
流设置超时($fs,30);
而(!feof($fs)){
$contents=fread($fs,4096);//缓冲下载
fwrite($fm,$contents);
$info=流获取元数据($fs);
如果($info['timed_out'])){
打破
}
}
fclose($fm);
现金流量表(fs);
如果($info['timed_out'])){
//如果失败,请删除临时文件
取消链接($temp\u file\u name);
$this->writeDebugInfo(“尝试失败”。$download\u尝试”-连接超时:”,$temp\u file\u name);
$download_trunt++;
如果($download\u尝试<5){
$this->writeDebugInfo(“重试:,$temp\u文件名”);
}
}否则{
//如果成功,则移动临时文件
$media_file_name=str_replace('temp/','media/',$temp_file_name);
重命名($temp\u file\u name,$media\u file\u name);
$this->newDownload=true;
$this->writeDebugInfo(“成功:”,$media\u file\u name);
}
}
}而($download_trunt<5&&$info['timed_out']);

感谢您的反馈。1) 我确实使用了CURL和CURLOPT_文件,但在传输过程中,我看不到数据超时会如何工作。我考虑在main while()循环中设置时间限制并捕获异常,但还是回到了fsockopen。2) 我正在检索二进制数据(压缩媒体),偶尔会出错。fread()看起来像一个直接的替代品。3) 任何额外的压缩都会有帮助,所以我来看看这个。我读过关于分块编码的mroe,但仍然看不到直接的好处。非常感谢您的回复。在CURL中暂停应该很容易。我建议您回到CURL,如果您对超时有问题,请问另一个更具体的问题。还有,我想你误读了我写的东西。您不需要分块编码。如果服务器支持,Gzip编码(不同)应该压缩输出。无论如何,CURL会处理好所有这些。我会检查CURL wrt超时。我知道不需要分块编码,但我只是读了一些关于这个主题的文章,以提高我对它的认识。我很乐意不用它。看起来需要使用CURLOPT_PROGRESSFUNCTION回调来检查CURL超时。这个CURLOPT仅在我没有使用的PHP5.3+中可用。所以可能在下一个小版本中。我很惊讶没有发现CURLOPT_数据超时或类似的情况。也许我只是看不见,就像我以前尝试使用CURL时使用的一样,虽然我已经设置了CURLOPT_RETURNTRANSFER,但现在可以看到它不是必需的(我想我可以理解为什么…)。在发现这个问题之前,还玩了一个小时的PHP错误:事实上,如果文件太大,设置CURLOPT_RETURNTRANSFER会给你一个内存错误,因为当你设置返回值时,它会被保存在内存中,而不是写入一个文件。我使用此方法将网站从测试主机迁移到生产主机。虽然有些主机禁用了正在执行的tar或使用防火墙连接到其他服务器,但它的速度相当快。我知道当不使用CURLOPT_文件时,它更多地用于“$data=curl_exec($ch)”。记忆错误是一个很好的了解和避免-谢谢。