flush();$file = fopen($local_file, "r"); while(!feof($file)) {// send the current file part to the browserprint fread($file, round($download_rate * 1024));// flush the content to the browserflush();// sleep one secondsleep(1); }fclose($file);}else { die('Error: The file '.$local_file.' does not exist!');}

To avoid the risk of choosing themselves which files to download by messing with the request and doing things like inserting "../" into the "filename", simply remember that URLs are not file paths, and there's no reason why the mapping between them has to be so literal as "download.php?file=thingy.mpg" resulting in the download of the file "thingy.mpg".

It's your script and you have full control over how it maps file requests to file names, and which requests retrieve which files.

But even then, as ever, never trust ANYTHING in the request. Basic first-day-at-school security principle, that.

It can be slow for big files to read by fread, but this is a single way to read file in strict bounds. You can modify this and add fpassthru instead of fread and while, but it sends all data from begin --- it would be not fruitful if request is bytes from 100 to 200 from 100mb file.

To anyone that's had problems with Readfile() reading large files into memory the problem is not Readfile() itself, it's because you have output buffering on. Just turn off output buffering immediately before the call to Readfile(). Use something like ob_end_flush().

Using pieces of the forced download script, adding in MySQL database functions, and hiding the file location for security was what we needed for downloading wmv files from our members creations without prompting Media player as well as secure the file itself and use only database queries. Something to the effect below, very customizable for private access, remote files, and keeping order of your online media.

While at it I added into download.php a hit (download) counter. Of course you need to setup the DB, table, and columns. Email me for Full setup// Session marker is also a security/logging option
Used in the context of linking:
http://www.yourdomain.com/download.php?id=xx&hit=1

When using the readfile_chunked function noted here with files larger than 10MB or so I am still having memory errors. It's because the writers have left out the all important flush() after each read. So this is the proper chunked readfile (which isn't really readfile at all, and should probably be crossposted to passthru(), fopen(), and popen() just so browsers can find this information):

When the popular IE setting “Reuse Window for Launching Shortcuts” is unchecked (access this setting in the Tools Menu > Internet Options > Advanced Tab) this script will output the file to the browser and open it in a different window if the user clicks the open button on the IE prompt. However, if this setting is checked, and browser windows are being re-used, then it will open up on top of the page where the link was clicked to access the script.

If I instead set the html link target option to be “_blank”, the script will open up in a new window as expected if the “Reuse Window for Launching Shortcuts” is checked. But, if the setting is unchecked, the output XML file will open up in a new window and there will be another blank window also open that has the address of the script, in addition to our original window.

This is far from ideal, and there is no way of knowing whether users have this option checked or not. We are stuck with the distinct possibility of half of our visitors seeing either an annoying third blank window being opened or the script writing over their original window, depending on their “Reuse Window for Launching Shortcuts” setting.

If you are lucky enough to not be on shared hosting and have apache, look at installing mod_xsendfile.This was the only way I found to both protect and transfer very large files with PHP (gigabytes). It's also proved to be much faster for basically any file.Available directives have changed since the other note on this and XSendFileAllowAbove was replaced with XSendFilePath to allow more control over access to files outside of webroot.

To reduce the burden on the server, you might want to output "Etag" and/or "Last-Modified" on http response header. But there are some headers, which PHP itself outputs automatically, disturbing this. So I wrote this function with erasing these.

If you guys know how to judge the return values of function "stat", in order to avoid using "is_file" or "is_readable" (or "is_dir"), please let me know or just write it here.

If you don't have to do anything special on 404, "header('HTTP/1.x xxx xxxxx');" can be inside of the function.

The streams API in PHP5 tries to make things as efficient as possible; in php-5.1.6 on Linux, fpassthru is faster than 'echo fread($fp, 8192)' in a loop, and readfile is even faster for files on disk. I didn't benchmark further, but I'd be willing to bet non-mmap'able streams still win because they can loop in C instead of PHP.

I wasted days trying to figure this out before I found the problem was easily solved.

I'm sure many of you out there have had similar problem when trying to use readfile to output images with a php file as the "src" of a "img" tag.
It works fine "as is" in Firefox but not in IE, Safari or g.Chrome.

I found hundreds of results on google all saying things like "there must be white space at the end of you code", "you need this header or that header".
I couldn't believe what the solution was but here it is anyway!

I think that readfile suffers from the maximum script execution time. The readfile is always completed even if it exceed the default 30 seconds limit, then the script is aborted.Be warned that you can get very odd behaviour not only on large files, but also on small files if the user has a slow connection.

The best thing to do is to use

<? set_time_limit(0);?>

just before the readfile, to disable completely the watchdog if you intend to use the readfile call to tranfer a file to the user.

Beware of using download managers.. I was trying to use readfile in IE8 and kept getting the message "failed to get data for 'type'". Eventually figured out the problem was that I had LeechGet installed and it was intercepting the download, which in turn prevented the download from taking place.

If you are using the procedures outlined in this article to force sending a file to a user, you may find that the "Content-Length" header is not being sent on some servers.

The reason this occurs is because some servers are setup by default to enable gzip compression, which sends an additional header for such operations. This additional header is "Transfer-Encoding: chunked" which essentially overrides the "Content-Length" header and forces a chunked download. Of course, this is not required if you are using the intelligent versions of readfile in this article.

A missing Content-Length header implies the following:

1) Your browser will not show a progress bar on downloads because it doesn't know their length2) If you output anything (e.g. white space) after the readfile function (by mistake), the browser will add that to the end of the download, resulting in corrupt data.

The easiest way to disable this behaviour is with the following .htaccess directive.

If you know the target _can't_ be a remote file (e.g. prefixing it with a directory), you should use include instead.If the user manages to set the target to some kinda config-file (configuration.php in Joomla!), he will get a blank page - unless readfile() is used. Using include will just behave as a normal request (no output).For remote files however use readfile().

To use readfile() it is absolutely necessary to set the mime-type before. If you are using an Apache, it's quite simple to figure out the correct mime type. Apache has a file called "mime.types" which can (in normal case) be read by all users.