Page 1 of 1
tracking download counts
Posted: Thu Apr 03, 2003 7:30 am
by permutations
I want to track how many times people download a zip file that's on my site. Right now I'm counting the number of times that people click the "Download now" button, but the download could be cancelled so this isn't completely accurate. In the daily Webalizer statistics, there's a count for the number of accesses to this zip file. Is there a way to obtain this in real time (versus analyzing the daily server statistics)? What's the best way to track download counts?
Posted: Thu Apr 03, 2003 8:13 am
by d1223m
you could have the classic download script.
make a script called download.php which takes a GET variable containing the file to download. This script simply has to passthru the file and add one to the dowload count.
This means that all your links "somefile.zip" become "download.php?f=somefile.zip"
This way you may be able to use the php function connection_abort ( iirc ) that tells you if the user jumped ship
Posted: Thu Apr 03, 2003 8:17 am
by permutations
I'm not familiar with the "classic download script" so I'm having trouble following your description. Can you give me more details, or point me somewhere I can see an example of a download script that uses this approach?
Thank you.
Posted: Thu Apr 03, 2003 8:46 am
by d1223m
maybe something like this for your download.php
Code: Select all
<?
$fileDir='/path/to/downloadable/files/';
$fileName=$_GET['file'];
$completeFilePath=$fileDir.'/'.$fileName;
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
header("Content-type: application/octet-stream\nContent-Disposition: inline; filename="".$fileName.""\nContent-length: ".(string)(filesize($completeFilePath)));
$fd=fopen($completeFilePath,'r');
fpassthru($fd);
// add one to your file download counters here
?>
to check if the download was complete ( after a brief look at this page
http://www.php.net/manual/en/features.c ... ndling.php ) i think you need to register a shutdown function ( register_shutdown_function() ) and inside that function check the value of the function connection_aborted(). This will return true if the connection was aborted. To count only files that have been fully downloaded put the code to add one to your counters inside the shutdown function and only run it if connection_aborted()!=true;
Posted: Thu Apr 03, 2003 8:53 am
by permutations
Looking at the message three-up about "classic download script" a little more, I realize that's what I'm already doing. The problem with this approach is that it counts clicks on the download button, but there's no way to tell if the file was really downloaded. I'll have to read up on that connection_aborted() function. I hadn't heard of that.
What do these header commands do:
header('Cache-Control: no-cache, must-revalidate');
header('Pragma: no-cache');
header("Content-type: application/octet-stream\nContent-Disposition: inline;
Also, what is the reason for obtaining a file pointer and calling fpassthru? What does that accomplish?
Posted: Thu Apr 03, 2003 8:54 am
by Jim
I think he's pointing you toward something like this, assuming you keep a count of all downloads on a text file:
Link to file:
http://www.yourdomain.com/download.php?file=filename
Code: Select all
<?
/***** Download.php *****/
//First, set the file variable properly. File = name of file in string
$file = $_GET['file'];
if($file) {
//Check to see if that file exists
if(file_exists("/link/to/downloads/$file") == false) {
die("This file does not exist!")
} else {
//Open text file containing download count
$file = 'count.php';
$open = fopen($file , 'r');
$count = fread($handle , filesize($file));
fclose($open);
//Add one to the count
$newcount = $count+1;
//Open count file and write new count to it
$file = 'count.php';
$count = $newcount;
$open = fopen($file , 'w');
fwrite($open , $count);
fclose($open);
}
}
?>
Understand that the above is an outline of what I think will work, and isn't yet tested. Also note that you don't have to use a text file to do the count, nor do you have to do an enmasse count for all files downloaded. You can set it up to count only the downloads of each particluar file.
Hope this helps!
Posted: Thu Apr 03, 2003 9:01 am
by d1223m
>What do these header commands do:
>header('Cache-Control: no-cache, must-revalidate');
>header('Pragma: no-cache');
>header("Content-type: application/octet-stream\nContent-Disposition: inline;
To be honest i ripped the downbload script from fpassthru. these headers force the download box to be displayed.
>Also, what is the reason for obtaining a file pointer and calling fpassthru? >What does that accomplish?
I thought to use it so you can actually see if the download has finished. If you just point them at a file ( eg somefile.zip ) then how can you tell they have finished the download? You cant with connection_aborted as its not a php script!
This soloution uses the php script to pass the file thru php just like you would send html normally ( altho the headers let the browser knwo its a download ). Also because of the "power" of php we can detect an aborted connection ( unlike just our regular somefile.zip ).
Providing just a link to somefile.zip means that your power of control stops as soon as they click the link. The passthru method gives you control to the very end *grin*
Posted: Thu Apr 03, 2003 10:42 am
by permutations
Sorry to be dense, but what do you mean you "ripped the download script from fpassthru". Is "fpassthru" a place in this context? Where did you see the script?
I'm still not understanding how using fpassthru to read the file can tell me whether the user has completed the download. I read the description of fpassthru, and it seems to just return the number of bytes in the file. What can I do with this? How does it tell me when the download has finished?
By the way, I store the download count in a MySQL database, rather than a text file.
... Oh wait, I think you mean the user notes on the fpassthru page of the php.net manual, yes? I see reference to it there. I need to understand what these lines do. Manual time...
Posted: Thu Apr 03, 2003 2:33 pm
by Jim
I think those headers simply keep the page from caching information that might be important in keeping the count on the download page correct.
Look at my solution and see if it makes any sense to you. I'm not sure whether or not I have something there, but I must admit I'm very interested in seeing exactly what that code might do.
I wish I weren't at school right now, or I'd do some testing on my computer

Posted: Thu Apr 03, 2003 2:59 pm
by permutations
Good grief, this was a pain in the neck--mainly because of IE, which requires special handling. But I did find a working solution. Here it is:
Code: Select all
<?php
$fileDir = '/home/mydir/public_html/files/';
$fileName = 'myfile.zip';
$completeFilePath=$fileDir.$fileName;
// IE cannot download without a cache, so clear these headers
if(strpos($HTTP_SERVER_VARSї'HTTP_USER_AGENT'], 'MSIE')){
header("Pragma: ");
header("Cache-Control: ");
}
else { //prevent caching
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
}
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename=".$fileName);
header("Content-Length: ".filesize("$completeFilePath"));
$fn=fopen($completeFilePath,'rb');
fpassthru($fn);
require_once('Connections/conMyDB.php');
mysql_select_db($database_conMyDB, $conMyDB);
// increase download count
$query = "UPDATE utilities SET DLCount=(DLCount+1) WHERE UtilityID = $UtilityID";
$rs = mysql_query($query, $conMyDB);
?>
One other comment... fpassthru() loads the entire file into memory, which can be a problem for large files. In that case, it's best to replace fpassthru with this:
Code: Select all
while(!feof($fn)) { // this replacement for fpassthru uses less memory (for large files)
$buffer = fread($fn, 4096);
print $buffer;
}
I tested all this and it works perfectly in all the browsers I tested (IE6, Netscape 4, Mozilla aka Netscape 7).
Posted: Fri Apr 04, 2003 6:27 am
by d1223m
yey youve got it!
now to detect broken downloads you can do something like this
i think:
Code: Select all
function onshutdown() {
if (connection_aborted()) {
// the download was aborted so dont add one to the download
} else {
// the download was ok so add one to the download count
}
}
register_shutdown_function(onShutdown);
im not 100% sure it will work but from what i understand of register_shutdown_fnuction,connection_aborted and fpassthru it should be
Posted: Fri Apr 04, 2003 6:39 am
by permutations
I don't need to do anything else to detect broken downloads. The counter is only increased if the download is completed, which is what I was after.
If you hadn't pointed me to the fpassthru page in the PHP manual, I wouldn't have found this solution. So thank you.
Posted: Fri Apr 04, 2003 6:58 am
by d1223m
lol - well im glad we got there.
i know ill want to do the same thing someday