PHP making a download counter without leaving the current page
Ok as you have most of the code missing here is an example, basically your need to call the counter code inside the download.php
file, and pass through the file contents after doing the counter code and setting download headers. also be wary or allowing malicious people to download any file from your server by just passing the file name to the download function. download.php?file=index.php
ect
<?php
function get_download_count($file=null){
$counters = './counters/';
if($file == null) return 0;
$count = 0;
if(file_exists($counters.md5($file).'_counter.txt')){
$fp = fopen($counters.md5($file).'_counter.txt', "r");
$count = fread($fp, 1024);
fclose($fp);
}else{
$fp = fopen($counters.md5($file).'_counter.txt', "w+");
fwrite($fp, $count);
fclose($fp);
}
return $count;
}
?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title></title>
</head>
<body>
<a href="./download.php?file=exampleA.zip">exampleA.zip</a> (Downloaded <?php echo get_download_count('exampleA.zip');?> times)<br>
<a href="./download.php?file=exampleB.zip">exampleB.zip</a> (Downloaded <?php echo get_download_count('exampleB.zip');?> times)<br>
</body>
</html>
download.php as you can see it outputs no HTML as this would corrupt the file.
<?php
//where the files are
$downloads_folder = './files/';
$counters_folder = './counters/';
//has a file name been passed?
if(!empty($_GET['file'])){
//protect from people getting other files
$file = basename($_GET['file']);
//does the file exist?
if(file_exists($downloads_folder.$file)){
//update counter - add if dont exist
if(file_exists($counters_folder.md5($file).'_counter.txt')){
$fp = fopen($counters_folder.md5($file).'_counter.txt', "r");
$count = fread($fp, 1024);
fclose($fp);
$fp = fopen($counters_folder.md5($file).'_counter.txt', "w");
fwrite($fp, $count + 1);
fclose($fp);
}else{
$fp = fopen($counters_folder.md5($file).'_counter.txt', "w+");
fwrite($fp, 1);
fclose($fp);
}
//set force download headers
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Content-Transfer-Encoding: binary');
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . sprintf("%u", filesize($downloads_folder.$file)));
//open and output file contents
$fh = fopen($downloads_folder.$file, "rb");
while (!feof($fh)) {
echo fgets($fh);
flush();
}
fclose($fh);
exit;
}else{
header("HTTP/1.0 404 Not Found");
exit('File not found!');
}
}else{
exit(header("Location: ./index.php"));
}
?>
Make sure you check the $downloads_folder
variable is correct. Hope it helps.
Download Full example code.
file download counter without database
if you are not familiar with database, try something like this
http://www.kavoir.com/2010/05/simplest-php-hit-counter-or-download-counter-count-the-number-of-times-of-access-visits-or-downloads.html
it may help you
How to create a download counter
If you want to be able to poll how many global users have downloaded the item and update the frontend without refreshing the page you will either need to use sockets or setInterval():
Here's how you would do it with setInterval() and updating the numbers every 3 seconds (you don't want to do it too much or it will be a big load on your server):
<ul>
<?php
foreach($files_array as $key => $val) {
echo "<li id=\"download-$key\">";
echo '<a class="btn btn-primary btn-large" href="download.php?file=' . urlencode($val) . '"><span class="download-text">Download</span></a><span class="download-count" title="Times Downloaded">' . (int)$file_downloads[$val] . '</span>';
echo '</li>';
}
?>
</ul>
<script>
setInterval(function() {
$.getJSON('/download-count.php', function(e) {
var i;
for (i in e) {
if (e.hasOwnProperty(i)) {
$('#download-' + i + ' .download-count').text(e[i]);
}
}
});
}, 3000); // ping every 3 seconds
</script>
You'll need a new file for your Ajax (in my example I called it /download-count.php
) that will echo out the JSON for your numbers
echo json_encode($file_downloads);
This is obviously a simplistic example but hopefully it gets the point across.
UPDATE: The download-count.php file will need to look something like this:
<?php
/**
* You don't show how you populate $file_downloads in your question but
* however you do it, do it the same way here
*/
$file_downloads = foo();
echo json_encode($file_downloads);
die();
How to force download and count downloads in PHP
You don't have to keep track of count
because you can simply count the lines of the file to see how many downloads there have been. https://stackoverflow.com/a/2162528/296555
if(time() - $_SESSION['time'] < 2000 || ($refData['host'] == 'partnersite.com')) {
// Sudo code - see real example here - https://stackoverflow.com/a/19898993/296555
// Write to log file before initiating the download.
$log = current date time
file_put_contents('./log_file.log', $log, FILE_APPEND);
// Now, do the download part
...
Best way to implement a download counter?
In the past I have done this:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
flush()
log()
exit;
log should only run after readfile is complete meaning the entire file has been streamed to the user. It's been a while since I have done this and I don't know where the exact code is so this may not be exact but it should get you going.
EDIT I found the above code on php.net and it is more or less what I was describing, this method should work.
PHP - Download file(s) from the server without revealing the URL - A few GOTCHAS
To get this working in my own code, I needed to wrap my PHP variables in either single or double quotes (and sometimes doubled single quotes) as shown below:
header('Content-Type:'.$ctype.'' );
header('Content-Disposition: attachment; filename="'.$fname.'"');
readfile(''.$filepath.'');
When entered this way, I was able to download the files without corruption or issue. Please let me know if you are aware of a better way of doing this.
However, when I used the coded above it also allowed me to NOT use the
ob_clean();
ob_end_flush();
commands in my code. In fact if I add them now . . . it seems to corrupt my files too. Weird.
Another potential GOTCHA is that if you attempt to download a TXT file, and you have a PHP "echo" statement anywhere in your "download.php" file, the contents of the ECHO statement will be appended to the TXTY file that's downloaded. So just something to be careful of.
This ECHO statement may also be appended to the headers of other documents types as well, but it didn't seem to affect the ability of the file to open in my limited testing. But again, be careful out there! :)
Download count only updates and displays the first row
All rows in your table have same span id
and when you perform $("#dl_count").html(data);
Your browser finds the first span with id = dl_count and changes its html content.
Here's a quick JSfiddle that would help you understand your problem JS fiddle
You can solve this problem by having individual id
for each row, something like concatenating id name with $i
, like this #dl_count . $is
.
Or
You can use the field data-rid
to distinguish each row.
HTML download counter without PHP
PHP is the most common server language available on hosting services, if your server does not allow it, it's possible you can't use any language at all on the server side.
Let's assume you can't use any language on the server side, then there is two possible actions.
- use a third party server where you can save your data.
- save locally your data using javascript.
Using a 3rd party service might be complex to implement and you need to learn a bit about cross origin request. You will need to add a few javascript librairies and understand a lots of concept so I'll just go with the easy one.
You browser have a localStorage wich can be access through Javascrip
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
[!] Know that this will be save only on your browser therefore other users or session will not have access to the counter.
// get the saved value or zero if not foundvar count = localStorage.getItem('so-demo') || 0;
// increment the value by 1count++;
// save the valuelocalStorage.setItem('so-demo', count);
// show the actual valuedocument.getElementById('theValue').innerHTML = count
<div id="theValue">localStorage is not allowed on stack overflow but works elsewhere</div>
how to redirect to linkpage after download
Try the following to download the file.
header('Content-Disposition: attachment; filename="'.$Down.'"');
Related Topics
Follow Redirects with Curl in PHP
How to Add Custom Fonts in Tcpdf
How Check Memory Location of Variable in PHP
JSON Encode an Entire MySQL Result Set
Ftp Upload File to Distant Server with Curl and PHP Uploads a Blank File
Send Email with Attachments in PHP
Get Specific Device Information
Customize Tax Amount in "Woocommerce_Package_Rates" Hook
How to Take a Screenshot of a Website with PHP and Gd
JSON_Encode Won't Encode French Characters
Creating a Secure File Hosting Server for PDFs
No Mail Received in Inbox with Xampp 1.8.0, Mercurymail and Mail()
Passing a Boolean Value from Checkbox in Laravel Form
Mod_Rewrite to Remove .PHP But Still Serve the .PHP File
Can Mongodb and Its Drivers Preserve the Ordering of Document Elements