Reputation: 339
How do I stop a remote file from downloading a file if it exceeds 5mb? If I stop it while transferring, will the file be held somewhere in some other temp dir or memory? How would I know? Here is my current code:
$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$file = '../temp/test.jpg';
file_put_contents($file, file_get_contents($url));
Upvotes: 7
Views: 2137
Reputation: 88707
There are a number of ways in which you could do this, but because you are currently using file_get_contents()
, here is what I would do:
fopen()
fread()
- this allows you to track the current size and throw and error when you pass the threshold.Something like this:
$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$file = '../temp/test.jpg';
$limit = 5 * 1024 * 1024; // 5MB
if (!$rfp = fopen($url, 'r')) {
// error, could not open remote file
}
if (!$lfp = fopen($file, 'w')) {
// error, could not open local file
}
// Check the content-length for exceeding the limit
foreach ($http_response_header as $header) {
if (preg_match('/^\s*content-length\s*:\s*(\d+)\s*$/', $header, $matches)) {
if ($matches[1] > $limit) {
// error, file too large
}
}
}
$downloaded = 0;
while (!feof($rfp) && $downloaded < $limit) {
$chunk = fread($rfp, 8192);
fwrite($lfp, $chunk);
$downloaded += strlen($chunk);
}
if ($downloaded > $limit) {
// error, file too large
unlink($file); // delete local data
} else {
// success
}
NB: it is tempting to inspect the Content-Length:
header before you receive any of the file to check whether the file is too large - you can still do this if you like, but do not trust the value! The header is essentially an arbitrary value, and while it would be a protocol violation to use a content length that does not match the size of the file, it could be used to fool the system into downloading a file that breaks the rules. You'd need count the bytes even if you do check this value.
You could also do this using curl via the data callback, but I would regard this as a less satisfactory solution, mostly because of fact that curl requires a string function name and not the standard generic callable
type, which means you would either need to use a global variable or a static
variable in order to keep track of the downloaded content length, neither of which are acceptable (IMHO) for this.
Upvotes: 8
Reputation: 38318
This isn't possible with file_get_contents
- you'll need to use the PHP cURL extension to grab the file size.
<?php
$max_length = 5 * 1024 * 1024;
$url = 'http://www.spacetelescope.org/static/archives/images/large/heic0601a.jpg';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$data = curl_exec($ch);
curl_close($ch);
if ($data === false) {
echo 'cURL failed';
exit;
}
$contentLength = 'unknown';
if (preg_match('/Content-Length: (\d+)/', $data, $matches)) {
$contentLength = (int)$matches[1];
}
echo "Content-Length: $contentLength\n";
if ($contentLength > $max_length) {
echo "File is too large\n";
} else {
echo "File size is ok\n";
}
Wrap this up in a function and use it to check a URL - anything too large can be skipped.
Downside:
This method only works if the Content-Length
header is returned by the server - not all servers are well-behaved and not all will return a correct length.
Upside:
There's no need to download and write up to $max_length
bytes. This method will be faster and less resource intensive.
Modified from:
Upvotes: 1
Reputation: 15381
Here's a variation on DaveRandom's solution along the same lines but without writing the file until it's completely loaded.
<?php
define('MAXMB', 5);
define('BYTESPERMB', 1048576);
$maxsize = MAXMB * BYTESPERMB;
$handle = fopen("http://www.example.com/", "rb");
$contents = '';
$completed = true;
while (!feof($handle)) {
$chunk = fread($handle, 8192);
$size += strlen($chunk);
if ($size > $maxsize) {
$completed = false;
break;
}
$contents .= $chunk;
}
fclose($handle);
if ($completed) {
// File appears to be completely downloaded so write it --
file_put_contents('path/to/localfile', $contents);
} else {
// It was too big
}
Upvotes: 1
Reputation: 86545
As for taking a URL and limiting the size, it's not too hard...but file_get_contents
won't do it. You could fopen
the url, and use stream_get_meta_data
on the stream to get an array of info about the stream. If i'm reading the example right (and if the transfer encoding isn't "chunked"), you'll find the Content-Length
header in there, which will tell you the file size. If the value is set, and is over your limit, bail.
If the stream metadata doesn't have that info, or the info says the file's small enough, you still have the stream itself; you can still fread
from the remote file and fwrite
to the local one in chunks til you read it all or hit the limit. Of course, if you get to that point, the file will still exist once you hit the limit; you'll have to delete it yourself.
Upvotes: 2