(PHP 4, PHP 5)
fread — Binary-safe file read
$handle
, int $length
)
fread() reads up to
length
bytes from the file pointer
referenced by handle
. Reading stops as soon as one
of the following conditions is met:
length
bytes have been read
handle
A file system pointer resource that is typically created using fopen().
length
Up to length
number of bytes read.
Returns the read string or FALSE
on failure.
Example #1 A simple fread() example
<?php
// get contents of a file into a string
$filename = "/usr/local/something.txt";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);
?>
Example #2 Binary fread() example
On systems which differentiate between binary and text files (i.e. Windows) the file must be opened with 'b' included in fopen() mode parameter.
<?php
$filename = "c:\\files\\somepic.gif";
$handle = fopen($filename, "rb");
$contents = fread($handle, filesize($filename));
fclose($handle);
?>
Example #3 Remote fread() examples
When reading from anything that is not a regular local file, such as streams returned when reading remote files or from popen() and fsockopen(), reading will stop after a packet is available. This means that you should collect the data together in chunks as shown in the examples below.
<?php
// For PHP 5 and up
$handle = fopen("www.example.com/", "rb");
$contents = stream_get_contents($handle);
fclose($handle);
?>
<?php
$handle = fopen("www.example.com/", "rb");
if (FALSE === $handle) {
exit("Failed to open stream to URL");
}
$contents = '';
while (!feof($handle)) {
$contents .= fread($handle, 8192);
}
fclose($handle);
?>
Note:
If you just want to get the contents of a file into a string, use file_get_contents() as it has much better performance than the code above.
Note:
Note that fread() reads from the current position of the file pointer. Use ftell() to find the current position of the pointer and rewind() to rewind the pointer position.
I had a fread script that hanged forever (from php manual):
<?php
$fp = fsockopen("example.host.com", 80);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "Data sent by socket");
$content = "";
while (!feof($fp)) { //This looped forever
$content .= fread($fp, 1024);
}
fclose($fp);
echo $content;
}
?>
The problem is that sometimes end of streaming is not marked by EOF nor a fixed mark, that's why this looped forever. This caused me a lot of headaches...
I solved it using the stream_get_meta_data function and a break statement as the following shows:
<?php
$fp = fsockopen("example.host.com", 80);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
fwrite($fp, "Data sent by socket");
$content = "";
while (!feof($fp)) {
$content .= fread($fp, 1024);
$stream_meta_data = stream_get_meta_data($fp); //Added line
if($stream_meta_data['unread_bytes'] <= 0) break; //Added line
}
fclose($fp);
echo $content;
}
?>
Hope this will save a lot of headaches to someone.
(Greetings, from La Paz-Bolivia)
For anyone still trying to write an effective file downloader function/script, the work has been done for you in all the major servers including Apache & nginx.
Using the X-Sendfile header, you can do the following:
if ($user->isLoggedIn())
{
header("X-Sendfile: $path_to_somefile_private");
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"$somefile\"");
}
Apache will serve the file for you while NOT revealing your private file path! Pretty nice. This works on all browsers/download managers and saves a lot of resources.
Documentation:
Apache module: https://tn123.org/mod_xsendfile/
Nginx: wiki.nginx.org/XSendfile
Lighttpd: blog.lighttpd.net/articles/2006/07/02/x-sendfile/
Hopefully this will save you many hours of work.
I couldn't get some of the previous resume scripts to work with Free Download Manager or Firefox. I did some clean up and modified the code a little.
Changes:
1. Added a Flag to specify if you want download to be resumable or not
2. Some error checking and data cleanup for invalid/multiple ranges based on tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
3. Always calculate a $seek_end even though the range specification says it could be empty... eg: bytes 500-/1234
4. Removed some Cache headers that didn't seem to be needed. (add back if you have problems)
5. Only send partial content header if downloading a piece of the file (IE workaround)
<?php
function dl_file_resumable($file, $is_resume=TRUE)
{
//First, see if the file exists
if (!is_file($file))
{
die("<b>404 File not found!</b>");
}
//Gather relevent info about file
$size = filesize($file);
$fileinfo = pathinfo($file);
//workaround for IE filename bug with multiple periods / multiple dots in filename
//that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe
$filename = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ?
preg_replace('/\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1) :
$fileinfo['basename'];
$file_extension = strtolower($path_info['extension']);
//This will set the Content-Type to the appropriate setting for the file
switch($file_extension)
{
case 'exe': $ctype='application/octet-stream'; break;
case 'zip': $ctype='application/zip'; break;
case 'mp3': $ctype='audio/mpeg'; break;
case 'mpg': $ctype='video/mpeg'; break;
case 'avi': $ctype='video/x-msvideo'; break;
default: $ctype='application/force-download';
}
//check if http_range is sent by browser (or download manager)
if($is_resume && isset($_SERVER['HTTP_RANGE']))
{
list($size_unit, $range_orig) = explode('=', $_SERVER['HTTP_RANGE'], 2);
if ($size_unit == 'bytes')
{
//multiple ranges could be specified at the same time, but for simplicity only serve the first range
//tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt
list($range, $extra_ranges) = explode(',', $range_orig, 2);
}
else
{
$range = '';
}
}
else
{
$range = '';
}
//figure out download piece from range (if set)
list($seek_start, $seek_end) = explode('-', $range, 2);
//set start and end based on range (if set), else set defaults
//also check for invalid ranges.
$seek_end = (empty($seek_end)) ? ($size - 1) : min(abs(intval($seek_end)),($size - 1));
$seek_start = (empty($seek_start) || $seek_end < abs(intval($seek_start))) ? 0 : max(abs(intval($seek_start)),0);
//add headers if resumable
if ($is_resume)
{
//Only send partial content header if downloading a piece of the file (IE workaround)
if ($seek_start > 0 || $seek_end < ($size - 1))
{
header('HTTP/1.1 206 Partial Content');
}
header('Accept-Ranges: bytes');
header('Content-Range: bytes '.$seek_start.'-'.$seek_end.'/'.$size);
}
//headers for IE Bugs (is this necessary?)
//header("Cache-Control: cache, must-revalidate");
//header("Pragma: public");
header('Content-Type: ' . $ctype);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: '.($seek_end - $seek_start + 1));
//open the file
$fp = fopen($file, 'rb');
//seek to start of missing part
fseek($fp, $seek_start);
//start buffered download
while(!feof($fp))
{
//reset time limit for big files
set_time_limit(0);
print(fread($fp, 1024*8));