【问题标题】:Download File to server from URL从 URL 下载文件到服务器
【发布时间】:2011-04-25 17:02:11
【问题描述】:

嗯,这个看起来很简单,确实如此。将文件下载到服务器所需要做的就是:

file_put_contents("Tmpfile.zip", file_get_contents("http://someurl/file.zip"));

只有一个问题。如果你有一个大文件,比如 100mb。然后,您将耗尽内存,无法下载文件。

我想要的是一种在下载文件时将文件写入磁盘的方法。这样,我可以下载更大的文件,而不会遇到内存问题。

【问题讨论】:

  • 这是在您的服务器配置中设置的,据我所知,PHP 无法真正绕过它(直接 .ini 编辑除外)

标签: php http stream download


【解决方案1】:

自 PHP 5.1.0 起,file_put_contents() 支持通过将流句柄作为 $data 参数传递来逐段写入:

file_put_contents("Tmpfile.zip", fopen("http://someurl/file.zip", 'r'));

来自手册:

如果 data [即第二个参数] 是流资源,则该流的剩余缓冲区将被复制到指定文件。这与使用类似 stream_copy_to_stream().

(感谢Hakre。)

【讨论】:

  • 那不是我的第一选择。如果在 php.ini 中设置了allow_fopen_url Off(安全的好主意),你的脚本就会被破坏。
  • @idealmachine 我认为file_get_contents() 在这种情况下也不起作用(参见OP)。
  • @geoff 我很具体,我提到了你想要的功能。您可能想要有人为您编写代码 - 但我相信您自己也学到了一些东西。另外,如果我们要评论彼此的 SO 互动 - please accept some more answers :)
  • @alex:请查看编辑,随意合并。让我知道什么时候可以在此处删除此评论。
  • 'b' 标志也应该在大多数情况下与fopen 一起使用;防止对图像和其他非纯文本文件产生不利影响。
【解决方案2】:

尝试使用 cURL

set_time_limit(0); // unlimited max execution time
$options = array(
  CURLOPT_FILE    => '/path/to/download/the/file/to.zip',
  CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
  CURLOPT_URL     => 'http://remoteserver.com/path/to/big/file.zip',
);

$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);

我不确定,但我相信CURLOPT_FILE 选项会在提取数据时写入,即。没有缓冲。

【讨论】:

  • 通常情况下,这很好,但我在 Web 应用程序中有此代码,所以我不能确定用户是否会安装 cURL。但是,我确实对此投了赞成票。
  • @Geoff 是分布式网络应用吗?因为如果您控制托管,那么您的用户就无关紧要(cURL 是您服务器上的库)。
  • 没有。我不控制托管。这是一个任何人都可以拥有的分布式网络应用程序。
  • Curl 可能丢失。但几乎所有共享托管公司都默认安装了 CURL。我的意思是,我还没有看到没有。
  • 根据我的测试,您不能直接为 CURLOPT_FILE 分配文件路径。它必须是一个文件处理程序。首先,使用$fh = fopen('/path/to/download/the/file/to.zip', 'w'); 打开文件并在curl_close($ch); 之后使用fclose($fh); 关闭文件。并设置CURLOPT_FILE => $fh
【解决方案3】:
private function downloadFile($url, $path)
{
    $newfname = $path;
    $file = fopen ($url, 'rb');
    if ($file) {
        $newf = fopen ($newfname, 'wb');
        if ($newf) {
            while(!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}

【讨论】:

  • 感谢您的代码片段,但您能解释一下您的代码@xaav 吗?我在 php 方面并不完全出色。 1024*8 是什么?再次感谢。
  • @wMINOw 行的长度。
  • 具体表示一次最多读取8KB(1024字节/KB * 8),因为参数是以字节为单位的。只要该行
  • 为什么这不是最佳答案?
  • 您如何使用这种方法处理错误?如果返回 404 或者连接中断或超时怎么办?
【解决方案4】:
  1. 在目标服务器中创建一个名为“downloads”的文件夹
  2. 将[此代码]保存到.php文件并在目标服务器中运行

下载器:

<html>
<form method="post">
<input name="url" size="50" />
<input name="submit" type="submit" />
</form>
<?php
    // maximum execution time in seconds
    set_time_limit (24 * 60 * 60);

    if (!isset($_POST['submit'])) die();

    // folder to save downloaded files to. must end with slash
    $destination_folder = 'downloads/';

    $url = $_POST['url'];
    $newfname = $destination_folder . basename($url);

    $file = fopen ($url, "rb");
    if ($file) {
      $newf = fopen ($newfname, "wb");

      if ($newf)
      while(!feof($file)) {
        fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
      }
    }

    if ($file) {
      fclose($file);
    }

    if ($newf) {
      fclose($newf);
    }
?>
</html> 

【讨论】:

  • 这假设用户想要一个独立的脚本,而不是一个可以在现有 PHP 应用程序中工作的解决方案,我相信后者是 OP 和大多数其他人正在寻找的。对于想要了解这种方法的人来说,解释也会有所帮助。
  • 每当我尝试这个时,我传输的文件大小总是 50816,但我的文件大小比这个大.. 120MB.. 知道这是为什么吗?
  • set_time_limit (24 * 60 * 60); 必须放在循环中。它在脚本的开头没有效果。
【解决方案5】:
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);

【讨论】:

  • 您的回答非常简单且效果很好,帮助我解决了 cURL 无法获取文件的问题,这很有效。谢谢:)
  • 你可能想解释一下这实际上是做什么的。
  • 这并没有解决OP超出PHP内存限制的问题。
  • 这非常简单明了。对于文件较小或环境是本地开发的简单情况非常有用。
  • .xlsx 文件有什么想法吗?它存储的是 0 字节内存的空文件。
【解决方案6】:

我用这个来下载文件

function cURLcheckBasicFunctions()
{
  if( !function_exists("curl_init") &&
      !function_exists("curl_setopt") &&
      !function_exists("curl_exec") &&
      !function_exists("curl_close") ) return false;
  else return true;
}

/*
 * Returns string status information.
 * Can be changed to int or bool return types.
 */
function cURLdownload($url, $file)
{
  if( !cURLcheckBasicFunctions() ) return "UNAVAILABLE: cURL Basic Functions";
  $ch = curl_init();
  if($ch)
  {

    $fp = fopen($file, "w");
    if($fp)
    {
      if( !curl_setopt($ch, CURLOPT_URL, $url) )
      {
        fclose($fp); // to match fopen()
        curl_close($ch); // to match curl_init()
        return "FAIL: curl_setopt(CURLOPT_URL)";
      }
      if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_HEADER, $curlopt_header)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects) ) return "FAIL: curl_setopt(CURLOPT_MAXREDIRS)";

        return curl_exec($ch);
    } else {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_HEADER, true)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) return "FAIL: curl_setopt(CURLOPT_RETURNTRANSFER)";
        if( !curl_setopt($ch, CURLOPT_FORBID_REUSE, false)) return "FAIL: curl_setopt(CURLOPT_FORBID_REUSE)";
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
    }
      // if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
      // if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
      // if( !curl_setopt($ch, CURLOPT_HEADER, 0) ) return "FAIL: curl_setopt(CURLOPT_HEADER)";
      if( !curl_exec($ch) ) return "FAIL: curl_exec()";
      curl_close($ch);
      fclose($fp);
      return "SUCCESS: $file [$url]";
    }
    else return "FAIL: fopen()";
  }
  else return "FAIL: curl_init()";
}

【讨论】:

【解决方案7】:

PHP 4 & 5 解决方案:

readfile() 本身不会出现任何内存问题,即使在发送大文件时也是如此。 如果启用了 fopen 包装器,则 URL 可以用作此函数的文件名。

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

【讨论】:

  • 这没有回答问题,因为问题是关于在磁盘上写入而不是到输出缓冲区。
【解决方案8】:

有3种方式:

  1. file_get_contents 和 file_put_contents
  2. 卷曲
  3. 打开

你可以找到例子from here

【讨论】:

    【解决方案9】:

    prodigitalson 的 answer 对我不起作用。我得到了missing fopen in CURLOPT_FILEmore details

    这对我有用,包括本地网址:

    function downloadUrlToFile($url, $outFileName)
    {   
        if(is_file($url)) {
            copy($url, $outFileName); 
        } else {
            $options = array(
              CURLOPT_FILE    => fopen($outFileName, 'w'),
              CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
              CURLOPT_URL     => $url
            );
    
            $ch = curl_init();
            curl_setopt_array($ch, $options);
            curl_exec($ch);
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            curl_close($ch);
            return $httpcode;
        }
    }
    
            
    

    【讨论】:

      【解决方案10】:

      在php中使用一个简单的方法copy()

      copy($source_url, $local_path_with_file_name);
      

      注意:如果目标文件已经存在,则会被覆盖

      PHP copy() Function

      注意:您需要为目标文件夹设置权限 777。 下载到本地机器时使用此方法。

      特别说明:777 是基于 Unix 的系统中的权限,对所有者、组和所有人具有完全的读/写/执行权限。 通常我们将此权限授予不需要太多的资产在 Web 服务器上对公众隐藏。示例:图像文件夹。

      【讨论】:

      • 我永远不会在网络服务器上将 777 设置为权限,我将解雇任何有坏主意的网络开发人员。每时每刻,无处不在。小心点 !你不能这样做 !想想安全。遵循 OWASP 规则是不够的。善于思考简单的事情很重要。
      • @ThierryB。注意:我已经给出了本地路径。 & 这可用于内部应用程序。对问答事项有良好的阅读和理解。考虑不同的场景。这不被接受/最佳答案。每个问题都有不同的答案,其中有优缺点。示例供您理解:即使是斐波那契也有多种独特的解决方案,其中只有一个是最好的。其他的将在不同的场景中使用。
      • 好的,但是花时间思考最佳实践并在安全的地方实施它们将使您更好地理解必须实施的概念。也许如果入侵者在您的 ($) 家中,做一些陷阱或以最好的方式建造东西会让他有些头疼;)
      【解决方案11】:

      简单的解决方案:

      <?php 
      exec('wget http://someurl/file.zip');
      

      【讨论】:

      • @Netwons 确保 wget 在您的服务器中可用。
      • wget 可用于系统错误 ======> errorCode=1 SSL/TLS 握手失败:TLS 连接未正确终止。
      • 或错误 ======> 正在连接到 www.you.com (www.you.com)|178.79.180.188|:443... 已连接。
      【解决方案12】:

      最佳解决方案

      在系统中安装aria2c

       echo exec("aria2c \"$url\"")
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-28
        相关资源
        最近更新 更多