【问题标题】:Using file_get_contents vs curl for file size使用 file_get_contents vs curl 获取文件大小
【发布时间】:2014-04-26 08:02:51
【问题描述】:

我的服务器上运行了一个文件上传脚本,该脚本还具有远程上传功能。一切正常,但我想知道通过 URL 上传的最佳方式是什么。现在我正在使用fopen 从粘贴在名为“from”的文本框中的远程 url 获取文件。我听说 fopen 不是最好的方法。这是为什么呢?

我还使用 file_get_contents 从 URL 获取文件的文件大小。我听说curl 在这方面更好。为什么会这样,以及如何将这些更改应用到此脚本?

<?php
$from = htmlspecialchars(trim($_POST['from']));

if ($from != "") {
    $file = file_get_contents($from);
    $filesize = strlen($file);

    while (!feof($file)) {
        $move = "./uploads/" . $rand2;
        move_upload($_FILES['from']['tmp_name'], $move);

        $newfile = fopen("./uploads/" . $rand2, "wb");
        file_put_contents($newfile, $file);
    }
}
?>

【问题讨论】:

  • 你能举例说明$from是什么吗?是不是像http://example.com/some/file.jpg
  • 是的... from 基本上是一个名为“from”的文本框,日期类似于example.com/some/file.jpg
  • 您不需要使用feofmove_upload file_put_contents 一次写入整个文件。请参阅我的更新答案。
  • 如果您更新了代码,请更新您的问题。

标签: php performance curl file-upload


【解决方案1】:

您可以使用filesize 获取磁盘上文件的文件大小。

file_get_contents 实际上将文件放入内存,所以$filesize = strlen(file_get_contents($from)); 已经获得了文件,除了查找它的大小之外,您无需对其进行任何操作。你可以代替你fwrite打电话file_put_contents

请参阅:file_get_contentsfile_put_contents

curl 在您需要更多访问HTTP 协议时使用。在 StackOverflow 上使用 PHP 中的 curl 有很多问题和示例。

所以我们可以先下载文件,在这个例子中我将使用file_get_contents,获取它的大小,然后将文件放到本地磁盘的目录中。

$tmpFile = file_get_contents($from);
$fileSize = strlen($tmpFile);
// you could do a check for file size here
$newFileName = "./uploads/$rand2";
file_put_contents($newFileName, $tmpFile);

在您的代码中,您有 move_upload($_FILES['from']['tmp_name'], $move);,但 $_FILES 仅在您有 &lt;input type="file"&gt; 元素时适用,而您似乎没有。

附:您可能应该将文件名中允许的字符列入白名单,例如 $goodFilename = preg_replace("/^[^a-zA-Z0-9]+$/", "-", $filename) 这通常更易于阅读且更安全。

替换:

while (!feof($file)) {
    $move = "./uploads/" . $rand2;
    move_upload($_FILES['from']['tmp_name'], $move);

    $newfile = fopen("./uploads/" . $rand2, "wb");
    file_put_contents($newfile, $file);
}

与:

$newFile = "./uploads/" . $rand2;
file_put_contents($newfile, $file);

整个文件由file_get_contents读入整个文件由file_put_contents写入

【讨论】:

  • “你可以用 fwrite call file_put_contents 代替你”是什么意思?我如何获得文件大小比?你能告诉我编辑后的代码吗?
  • @imbondbaby 从您上面的评论中,您必须运行 file_get_contents 来获取完整文件,然后您可以检查其大小。我会更新我的答案。
  • 如果您希望在if (($from != "") 中执行该检查,可以删除&amp;&amp; ($file = file_get_contents($from, "rb"),现在用我的代码替换$move = ...while 循环的末尾。
  • 我现在已经这样做了......我收到了这个错误警告:feof() 期望参数 1 是资源,给定字符串
  • 啊,去掉while循环后它现在可以正常工作了……为什么我们必须去掉while循环?这会影响我的上传脚本吗?
【解决方案2】:

据我了解您的问题:您想获取由 URL 给出的远程字段的文件大小,但您不确定哪种解决方案最好/最快。

首先,CURLfile_get_contents()fread() 在这种情况下最大的区别是 CURL 和 file_get_contents() 将整个内容放入内存,而 fopen() 让您可以更好地控制哪些部分您要阅读的文件。我认为 fopen() 和 file_get_contents() 在您的情况下几乎是等效的,因为您正在处理小文件并且您实际上想要获取整个文件。所以它在内存使用方面没有任何区别。

CURL 只是 file_get_contents() 的老大哥。它实际上是一个完整的 HTTP-Client,而不是某种简单功能的包装器。

关于 HTTP:不要忘记 HTTP 不仅仅是 GET 和 POST。你为什么不直接使用资源的元数据来检查它的大小你甚至得到它?这是 HTTP 方法 HEAD 的用途之一。 PHP 甚至带有一个用于获取标题的内置函数:get_headers()。但是它有一些缺陷:它仍然发送一个 GET 请求,这可能会慢一点,并且它遵循重定向,这可能会导致安全问题。但是你可以通过调整默认上下文很容易地解决这个问题:

$opts = array(
    'http' =>
        array(
            'method' => 'HEAD',
            'max_redirects'=> 1,
            'ignore_errors'=> true
        )
);

stream_context_set_default($opts);

完成。现在您可以简单地获取标题:

$headers = get_headers('http://example.com/pic.png', 1);

//set the keys to lowercase so we don't have to deal with lower- and upper case
$lowerCaseHeaders = array_change_key_case($headers);

// 'content-length' is the header we're interested in:
$filesize = $lowerCaseHeaders['content-length'];

注意:filesize()在 http / https 流包装器上工作,因为不支持 stat() (http://php.net/manual/en/wrappers.http.php)。

差不多就是这样。当然,如果您更喜欢它,您也可以achieve the same with CURL 一样简单。该方法将是相同的(红色标题)。

以下是使用 CURL 获取文件及其大小(下载后)的方法:

// Create a CURL handle
$ch = curl_init();

// Set all the options on this handle
// find a full list on 
// http://au2.php.net/manual/en/curl.constants.php
// http://us2.php.net/manual/en/function.curl-setopt.php (for actual usage)
curl_setopt($ch, CURLOPT_URL, 'http://example.com/pic.png');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// Send the request and store what is returned to a variable
// This actually contains the raw image data now, you could
// pass it to e.g. file_put_contents();
$data = curl_exec($ch);

// get the required info about the request
// find a full list on
// http://us2.php.net/manual/en/function.curl-getinfo.php
$filesize = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);

// close the handle after you're done
curl_close($ch);

纯 PHP 方法:http://codepad.viper-7.com/p8mlOt

使用 CURL:http://codepad.viper-7.com/uWmsYB

对于文件大小的格式良好且人类可读的输出,我从 Laravel 学到了这个惊人的功能:

function get_file_size($size)
{
  $units = array('Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB');
  return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2).' '.$units[$i];
}

如果您不想处理所有这些问题,请查看Guzzle。对于任何类型的 HTTP 内容,它都是一个非常强大且非常易于使用的库。

【讨论】:

  • 您的回答帮助我了解了很多差异......但是如果我想在我的代码中实现 CURL,我该怎么做?我了解您所说的文件大小我可以使用 curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD);但是实际获取文件呢?
  • @imbondbaby 实际上有大量关于 CURL 和 PHP 的教程。我用一个非常基本的解决方案编辑了我的答案。有关更多详细信息,请在 Google 上获取一些教程或阅读文档:au2.php.net/manual/en/book.curl.php
猜你喜欢
  • 1970-01-01
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多