【问题标题】:PHP cURL, read remote file and write contents to local filePHP cURL,读取远程文件并将内容写入本地文件
【发布时间】:2011-12-19 12:46:56
【问题描述】:

我想连接到远程文件并将远程文件的输出写入本地文件,这是我的功能:

function get_remote_file_to_cache()
{

    $the_site="http://facebook.com";

    $curl = curl_init();
    $fp = fopen("cache/temp_file.txt", "w");
    curl_setopt ($curl, CURLOPT_URL, $the_site);
    curl_setopt($curl, CURLOPT_FILE, $fp);

    curl_setopt($curl,  CURLOPT_RETURNTRANSFER, TRUE);

    curl_exec ($curl);

    $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
    if($httpCode == 404) {
        touch('cache/404_err.txt');
    }else
    {
        touch('cache/'.rand(0, 99999).'--all_good.txt');
    }

    curl_close ($curl);
}

它在“cache”目录下创建了两个文件,但问题是它没有将数据写入“temp_file.txt”,这是为什么呢?

【问题讨论】:

  • 我不认为你可以在同一个操作中设置CURLOPT_FILECURLOPT_RETURNTRANSFER

标签: php curl php-curl


【解决方案1】:

你有问题

    curl_setopt($curl, CURLOPT_FILE, $fp);

    curl_setopt($curl,  CURLOPT_RETURNTRANSFER, TRUE);

但来自 PHP 的 curl_setopt 文档说明...

It appears that setting CURLOPT_FILE before setting CURLOPT_RETURNTRANSFER doesn't work, presumably because CURLOPT_FILE depends on CURLOPT_RETURNTRANSFER being set.

So do this:

<?php
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FILE, $fp);
?>

not this:

<?php
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
?>

...声明“CURLOPT_FILE 取决于设置的 CURLOPT_RETURNTRANSFER”。

参考:https://www.php.net/manual/en/function.curl-setopt.php#99082

【讨论】:

  • 请注意,即使在使用 curl_setopt_array 时也是如此 - 您必须在数组中的 CURLOPT_FILE 之前列出 CURLOPT_RETURNTRANSFER
【解决方案2】:

为了避免内存泄漏问题:

我也遇到过这个问题。说起来真的很蠢,但是解决办法是在 CURLOPT_FILE 之前设置 CURLOPT_RETURNTRANSFER!

似乎 CURLOPT_FILE 取决于 CURLOPT_RETURNTRANSFER。

$curl = curl_init();
$fp = fopen("cache/temp_file.txt", "w+");
curl_setopt($curl,  CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($curl, CURLOPT_FILE, $fp);
curl_setopt($curl, CURLOPT_URL, $url);
curl_exec ($curl);
curl_close($curl);
fclose($fp);

【讨论】:

  • 您根本不需要 CURLOPT_RETURNTRANSFER。 CURLOPT_RETURNTRANSFER 将返回值设置为单个字符串; CURLOPT_FILE 改变了这种行为,而不是将返回值存储到单个字符串中,它会在运行时打印到文件中。这就是为什么在 CURLOPT_RETURNTRANSFER 之后有 CURLOPT_FILE 是可行的......但实际上你根本不需要 CURLOPT_RETURNTRANSFER。
【解决方案3】:

让我们尝试向http://facebook.com发送GET请求:

$ curl -v http://facebook.com * 重建网址:http://facebook.com/ * 在 DNS 缓存中未找到主机名 * 尝试 69.171.230.5... * 连接到 facebook.com (69.171.230.5) 端口 80 (#0) > 获取/HTTP/1.1 > 用户代理:curl/7.35.0 > 主办方:facebook.com > 接受:*/* >

发生了什么? Facebook 似乎将我们从http://facebook.com 重定向到安全https://facebook.com/。注意什么是响应正文长度:

内容长度:0

这意味着零字节将被写入xxxx--all_good.txt。这就是文件保持为空的原因。

您的解决方案完全正确:

$fp = fopen('file.txt', 'w');
curl_setopt($handle, CURLOPT_FILE, $fp);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);

您只需将 URL 更改为 https://facebook.com/

关于其他答案:

  • @JonGauthier:不,没有必要在curl_exec()之后使用fwrite()
  • @doublehelix:不,您不需要CURLOPT_WRITEFUNCTION 来执行将内容复制到文件这样简单的操作。
  • @ScottSaunders:touch() 如果不存在则创建空文件。我认为这是 OP 的意图。

说真的,三个答案,每一个都是无效的?

【讨论】:

  • 你说得对,就是这么简单。只要记得提前创建“file.txt”,并设置它的权限(例如777)。
  • 不要将权限设置为 777,将所有权限都授予所有人存在安全风险。正如安德烈所说,尽量不要将 CURLOPT_RETURNTRANSFER 与 CURLOPT_FILE 一起使用。我得到了 302 返回码,我只用 CURLOPT_FILE 尝试了 CURLOPT_FOLLOWLOCATION,现在没有空文件,我将数据写入文件。
  • 我必须删除 curl_setopt($handle, CURLOPT_RETURNTRANSFER, true); 才能正常工作。
【解决方案4】:

实际上,使用 fwrite 是部分正确的。 为了避免大文件的内存溢出问题(超过 PHP 的最大内存限制),您需要设置一个回调函数来写入文件。

注意:我建议创建一个专门用于处理文件下载和文件句柄等的类,而不是使用全局变量,但出于本示例的目的,下面展示了如何启动和运行。

所以,请执行以下操作:

# setup a global file pointer
$GlobalFileHandle = null;

function saveRemoteFile($url, $filename) {
  global $GlobalFileHandle;

  set_time_limit(0);

  # Open the file for writing...
  $GlobalFileHandle = fopen($filename, 'w+');

  $ch = curl_init();
  curl_setopt($ch, CURLOPT_URL, $url);
  curl_setopt($ch, CURLOPT_FILE, $GlobalFileHandle);
  curl_setopt($ch, CURLOPT_HEADER, 0);
  curl_setopt($ch, CURLOPT_USERAGENT, "MY+USER+AGENT"); //Make this valid if possible
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); # optional
  curl_setopt($ch, CURLOPT_TIMEOUT, -1); # optional: -1 = unlimited, 3600 = 1 hour
  curl_setopt($ch, CURLOPT_VERBOSE, false); # Set to true to see all the innards

  # Only if you need to bypass SSL certificate validation
  curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

  # Assign a callback function to the CURL Write-Function
  curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curlWriteFile');

  # Exceute the download - note we DO NOT put the result into a variable!
  curl_exec($ch);

  # Close CURL
  curl_close($ch);

  # Close the file pointer
  fclose($GlobalFileHandle);
}

function curlWriteFile($cp, $data) {
  global $GlobalFileHandle;
  $len = fwrite($GlobalFileHandle, $data);
  return $len;
}

您还可以创建一个进度回调来显示您正在下载多少/多快,但这是另一个示例,因为它在输出到 CLI 时可能会很复杂。

本质上,这将获取下载的每个数据,并立即将其转储到文件中,而不是先将整个文件下载到内存中。

这样做更安全! 当然,您必须确保 URL 正确(将空格转换为 %20 等)并且本地文件是可写的。

干杯, 詹姆斯。

【讨论】:

  • 在现代 PHP 中,可以通过以下方式使其更紧凑:“curl_setopt($ch, CURLOPT_WRITEFUNCTION, function ($cp, $data) use ($fp) { return fwrite($fp, $data ); });" (其中“$GlobalFileHandle”变为“$fp”)?它似乎对我有用,但我想检查行为是否相同。
  • 指定 CURLOPT_FILE 时不需要回调。我刚试过。它直接写入文件,而不是先将整个内容读入内存。
  • 尝试指向这个文件。您将看到已达到硬内存限制。 ipv4.download.thinkbroadband.com/1GB.zip Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65015808 bytes) 请记住,不同的环境对每个进程都有不同的默认内存限制。这在 Windows/Linux 之间也有所不同。
【解决方案5】:

touch() 函数不对文件的内容做任何事情。它只是更新修改时间。看file_put_contents() function

【讨论】:

    【解决方案6】:

    您需要使用fwrite 显式写入文件,并将您之前创建的文件句柄传递给它:

    if ( $httpCode == 404 ) {
        ...
    } else {
        $contents = curl_exec($curl);
        fwrite($fp, $contents);
    }
    
    curl_close($curl);
    fclose($fp);
    

    【讨论】:

    • 您会遇到大文件的内存限制。双螺旋检查响应,更安全。
    • @JonGauthier 这并不能解决您有内存限制并且您想避免将整个文件加载到内存中而只想将其转储到本地文件的问题。
    • 请去投票给这个页面上唯一的实际正确答案:stackoverflow.com/a/62617724/168123 在OP发布的代码中,cURL代码是正确的,除了RETURNTRANSFER选项在FILE之后选项。在这种情况下,cURL 会忽略 FILE 选项,并且下载的文件作为响应返回。这就是为什么关于使用fwrite 的所有其他答案似乎都是可行的解决方案,因为它们从FILE 选项的失败开始并在响应中使用文件(这也是他们必须处理内存错误的原因) .
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-29
    • 1970-01-01
    相关资源
    最近更新 更多