Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/228.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 cURL,读取远程文件并将内容写入本地文件_Php_Curl_Php Curl - Fatal编程技术网

PHP cURL,读取远程文件并将内容写入本地文件

PHP cURL,读取远程文件并将内容写入本地文件,php,curl,php-curl,Php,Curl,Php Curl,我想连接到远程文件并将远程文件的输出写入本地文件,这是我的功能: 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, $th

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

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”,这是为什么?

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

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

curl_close($curl);
fclose($fp);
touch()
函数对文件内容没有任何作用。它只是更新修改时间。看看。

实际上,使用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等),并且本地文件是可写的

干杯,
James。

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

$ curl -v http://facebook.com * Rebuilt URL to: http://facebook.com/ * Hostname was NOT found in DNS cache * Trying 69.171.230.5... * Connected to facebook.com (69.171.230.5) port 80 (#0) > GET / HTTP/1.1 > User-Agent: curl/7.35.0 > Host: facebook.com > Accept: */* > < HTTP/1.1 302 Found < Location: https://facebook.com/ < Vary: Accept-Encoding < Content-Type: text/html < Date: Thu, 03 Sep 2015 16:26:34 GMT < Connection: keep-alive < Content-Length: 0 < * Connection #0 to host facebook.com left intact 这意味着零字节将被写入
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\u exec()之后使用
    fwrite()
  • @doublehelix:不,您不需要使用
    CURLOPT\u WRITEFUNCTION
    来执行将内容复制到文件的简单操作
  • @ScottsUnders:
    touch()
    如果文件不存在,则创建空文件。我认为这是行动的意图

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

为了避免内存泄漏问题:

我也遇到了这个问题。说起来真的很愚蠢,但解决办法是在CURLOPT_文件之前设置CURLOPT_RETURNTRANSFER

似乎CURLOPT_文件依赖于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);

在你的问题中,你有

    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_RETURNTRANSFER之前设置CURLOPT_文件似乎不起作用,可能是因为CURLOPT_文件取决于正在设置的CURLOPT_RETURNTRANSFER。
这样做:
不是这个:
…声明“CURLOPT\u文件取决于设置的CURLOPT\u RETURNTRANSFER


参考资料:

我不认为你可以在同一个操作中设置
CURLOPT_FILE
CURLOPT_RETURNTRANSFER
。在现代PHP中,这可以通过“curl_setopt($ch,CURLOPT_WRITEFUNCTION,function($cp,$data)use($fp){return fwrite($fp,$data);”(其中“$GlobalFileHandle”变成“$fp”)来简化吗?它似乎对我有效,但我想检查行为是否相同。指定CURLOPT_文件时不需要回调。我刚试过。它直接写入文件,而不首先将整个内容读入内存。请尝试指向此文件。您将看到已达到硬内存限制<代码>致命错误:允许的内存大小134217728字节已用尽(尝试分配65015808字节)
请记住,不同的环境每个进程将有不同的默认内存限制。这在Windows/Linux之间也有所不同。对于大文件,您将遇到内存限制。请检查doublehelix的响应,这样更安全。@JonGauthier这并不能解决您的内存限制问题,您希望避免将整个文件加载到内存,而只想将其转储到本地文件。请在本页上投票选出唯一正确的答案:在OP发布的代码中,cURL代码是正确的,除了
RETURNTRANSFER
选项在
FILE
选项之后。在这种情况下,cURL忽略
FILE
选项,下载的文件作为响应返回。这就是为什么关于使用
fwrite
的所有其他答案看起来都像是可行的解决方案,因为它们是从
FILE
选项失败开始的,并且在响应中处理文件(这也是它们必须处理内存错误的原因)。你是对的,就这么简单。只需记住提前创建“file.txt”,并设置其权限(例如777)。不要将权限设置为777,将所有权限授予所有人会带来安全风险。尽量不要像安德烈所说的那样将CURLOPT_RETURNTRANSFER与CURLOPT_文件一起使用。我得到了302个返回码,我只使用CURLOPT_文件尝试了CURLOPT_FOLLOWLOCATION,现在没有空文件,我将数据写入文件。我必须删除
curl_setopt($handle,CURLOPT_RETURNTRANSFER,true)
使其工作。请注意,即使在使用
curl\u setopt\u数组
时也是如此-您必须在数组中的
CURLOPT\u文件
之前列出
CURLOPT\u RETURNTRANSFER
。您根本不需要CURLOPT\u RETURNTRANSFER。CURLOPT_RETURNTRANSFER将返回值设置为单个字符串;CURLOPT_文件改变了这种行为,它不再将返回值存储为单个字符串,而是在运行时打印到文件中。这就是为什么在CURLOPT_RETURNTRANSFER之后使用CURLOPT_文件的原因。。。但事实上你根本不需要转车。Thx@ATJ!我试试看。我注意到这种行为,但没有得到正确的解释。
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);
?>