Ok, I can't post the entire thing, but here's a download script that I wrote for a project a few months ago.
<?php
require_once("resources/includes/init.php");
if (isset($_GET['id'])){
if ($_GET['id'] != ""){
$id = HZ_Taint($_GET['id']);
$file = HZ_SelectFromTable("HZ_FileList", "WHERE file_id = '".$id."'");
if (!PEAR::isError($file)){
if (count($file) > 0){
if (count($file) > 1){//if we have more than one file with exactly the same name (should never happen)
header('Content-Type: text/plain');
echo "Error, file naming conflict, please contact admin with this error message and the file name you requested (".$name.")";
exit;
} else {
$file = $file[0];//reassign the first object in the returned array to $file since there is only one file
//if it really is a regular file
if (is_file($file->path)){
//if the requested file's path is not outside the base directory we set in config
if (substr(realpath($file->path), 0, strlen(BASE_FILE_UPLOAD_DIR)) == BASE_FILE_UPLOAD_DIR){
$file_data = file_get_contents($file->path);
$finfo = finfo_open(FILEINFO_MIME);
$finfo_mime = finfo_file($finfo, $file->path);
//the following headers ensure that the browser is forced to download the file, it should work on all major bowsers
header('Pragma: public');
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: pre-check=0, post-check=0, max-age=0');
header('Content-Transfer-Encoding: none');
//This should work for IE & Opera
header('Content-Type: '.$file->type.'; name="'.basename($file->name).'"');
//This should work for the rest
header('Content-Type: '.$file->type.'; name="'.basename($file->name).'"');
header('Content-Disposition: attachment; filename="'.basename($file->name).'"');
echo $file_data;
exit;
} //if substr
}//if is_file
}//if count == 1
}//if count > 0
}//if !PEAR::isError
}//if name != ""
}//if isset(name)
header("Content-Type: text/plain");
echo "The file you requested cannnot be found";
exit;
?>
It uses PEAR::DB, db initialization is in the init.php, HZ_SelectFromTable is a wrapper for table access (handles errors and some other things and returns an array, by default it uses DB_FETCHMODE_OBJECT), HZ_Taint is essentially a wrapper for mysql_real_escape_string (plus a few other checks).
It relies on the file info (NOT the file data) previously being inserted into the database with paths.
It wouldn't take much to add a bit of code to track downloads to it.