bondbaby11
bondbaby11

Reputation: 305

Using file_get_contents vs curl for file size

I have a file uploading script running on my server which also features remote uploads.. Everything works fine but I am wondering what is the best way to upload via URL. Right now I am using fopen to get the file from the remote url pasted in the text box named "from". I have heard that fopen isn't the best way to do it. Why is that?

Also I am using file_get_contents to get the file size of the file from the URL. I have heard that curl is better on that part. Why is that and also how can I apply these changes to this script?

<?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);
    }
}
?>

Upvotes: 4

Views: 18521

Answers (2)

Quasdunk
Quasdunk

Reputation: 15220

As far as I understand your question: You want to get the filesize of a remote fiel given by a URL, and you're not sure which solution ist best/fastest.

At first, the biggest difference between CURL, file_get_contents() and fread() in this context is that CURL and file_get_contents() put the whole thing into memory, while fopen() gives you more control over what parts of the file you want to read. I think fopen() and file_get_contents() are nearly equivalent in your case, because you're dealing with small files and you actually want to get the whole file. So it doesn't make any difference in terms of memory usage.

CURL is just the big brother of file_get_contents(). It is actually a complete HTTP-Client rather than some kind of a wrapper for simple functions.

And talking about HTTP: Don't forget there's more to HTTP than GET and POST. Why don't you just use the resource's meta-data to check it's size before you even get it? That's one thing the HTTP method HEAD is meant for. PHP even comes with a built in function for getting the headers: get_headers(). It has some flaws though: It still sends a GET request, which makes it probably a little slower, and it follows redirects, which may cause security issues. But you can fix this pretty easily by adjusting the default context:

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

stream_context_set_default($opts);

Done. Now you can simply get the headers:

$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'];

NOTE: filesize() will not work on a http / https stream wrapper, because stat() is not supported (http://php.net/manual/en/wrappers.http.php).

And that's pretty much it. Of course you can achieve the same with CURL just as easy if you like it better. The approach would be same (reding the headers).

And here's how you get the file and it's size (after downloading) with 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);

Pure PHP approach: http://codepad.viper-7.com/p8mlOt

Using CURL: http://codepad.viper-7.com/uWmsYB

For a nicely formatted and human readable output of the file size I've learned this amazing function from 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];
}

If you don't want to deal with all this, you should check out Guzzle. It's a very powerful and extremely easy to use library for any kind HTTP stuff.

Upvotes: 3

Victory
Victory

Reputation: 5890

You can use filesize to get the file size of a file on disk.

file_get_contents actually gets the file into memory so $filesize = strlen(file_get_contents($from)); already gets the file, you just don't do anything with it other than find it size. You can substitute for you fwrite call file_put_contents;

See: file_get_contents and file_put_contents .

curl is used when you need more access to the HTTP protocol. There are many questions and examples on StackOverflow using curl in PHP.

So we can first download the file, in this example I wll use file_get_contents, get its size, then put the file in the directory on your local disk.

$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);

In your code you have move_upload($_FILES['from']['tmp_name'], $move); but $_FILES is only applicable when you have a <input type="file"> element, which it doesn't seem you have.

P.S. You should probably white-list characters that you allow in a filename for instance $goodFilename = preg_replace("/^[^a-zA-Z0-9]+$/", "-", $filename) This is often easier to read and safer.

Replace:

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

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

With:

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

The whole file is read in by file_get_contents the whole file is written by file_put_contents

Upvotes: 11

Related Questions