Счетчик скачивания файлов

Счетчик скачивания файлов

От автора: при создании сайтов очень часто необходимо выкладывать на его страницах материалы для скачивания. При этом полезно знать, какой популярностью пользуется тот или иной файл, а для этого необходимо вести учет количества его скачиваний. Поэтому в данном уроке мы с Вами рассмотрим два варианта создания счетчика скачивания файлов.

скачать исходникискачать урок

Постановка задачи

Итак, в данном уроке нам с Вами необходимо реализовать счетчик скачивания файлов на страницах сайта. Значит, первым делом необходимо определиться, где мы будем хранить результат подсчета скачивания того или иного файла. На мой взгляд, наилучшим решением – является база данных. Поэтому создадим базу данных dc, в которой далее по ходу урока создадим две таблицы – по одной для каждого варианта реализации счетчика загрузок.

Вариант №1

Итак, для первого варианта создаем таблицу count1 в базе данных dc, с двумя полями:

name – поле для хранения имени файла (тип данных String). Важное замечание: имена файлов – должны быть уникальными, а значит нельзя допускать повторений в данном столбце. Поэтому данному полю необходимо присвоить индекс UNIQUE (если одному из полей таблицы присвоен данный индекс, значит в данном поле могут содержаться только уникальные данные, то есть вставка повторяющегося значения приведет к ошибке).

count – количество скачивания файла (тип данных Integer). Значение данного поля по умолчанию – 1.

SQL – запрос для создания таблицы в базе данных.

--
-- Структура таблицы `count1`
--

CREATE TABLE IF NOT EXISTS `count1` (
  `name` varchar(255) NOT NULL,
  `count` int(11) NOT NULL DEFAULT '1',
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Теперь давайте определимся с логикой работы скрипта. Изначально, в базе данных нет ни одной записи. Файлы для скачивания, сохранены в отдельной папке на нашем сайте (в моем случае – это папка files), и выводятся на его страницах в соответствии с его задачами и дизайном.

Для данного урока я создал страницу, которая выводит имена всех файлов, в виде ссылок расположенных в папке files. Важно — путь каждой ссылки – не должен вести непосредственно на файл. Потому как нам, необходимо вести учет скачивания. Значит каждая ссылка, должна перенаправлять пользователя на скрипт обработчик, то есть файл, который подсчитает количество скачивания определенного файла и отдаст его пользователю на скачивание. При этом имя файла, для скачивания, будем передавать, используя метод GET, то есть через адресную строку. Итак, первым делом создадим файл конфигурации d_conf.php:

<?php
//db//
define("HOST","localhost");
define("USER","root");
define("PASS","");
define("DB","dc");
//files//
define("DIR","files/");
//types//
$types = array('rar','zip','txt','pdf','jpg');
?>

В данном файле создадим константы с настройками для подключения к базе данных (HOST, USER, PASS, DB), так же добавим константу DIR – путь к файлам для скачивания. А также массив $types – типы фалов, допустимые для загрузки.

Далее создадим файл d_func.php, в котором будут описаны функции, необходимые для работы приложения в целом. Создадим первую функцию connect(), которая будет выполнять подключение к базе данных, используя расширение php – mysqli (улучшенный движок по работе с СУБД MySql). Поэтому данная функция будет возвращать идентификатор открытого соединения, который нам потребуется для дальнейшей работы. Код функции connect():

function connect() {
	$db = mysqli_connect(HOST,USER,PASS,DB);
	if(mysqli_connect_error($db)) {
		exit(mysqli_connect_error($db));
	}
	return $db;
}

Теперь, так как мы условились, что в таблицу count1, будет записана информация о скачиваемых файлах (имя и количество скачиваний), значит опишем следующую функцию get_count(), которая получит всю информацию из данной таблицы. И как результат вернет массив, в каждой ячейке которого будет содержаться имя файла (ключ ячейки) и количество его скачиваний (значение ячейки):

function get_count($db) {
	$query = "SELECT name,count FROM count1";
	$result = mysqli_query($db,$query);
	
	$arr = array();
	for($i = 0; $i < mysqli_num_rows($result);$i++) {
		$row = mysqli_fetch_assoc($result);
		$arr[$row['name']] = $row['count'];
	}
	
	return $arr;
}

Файл, который выводит список файлов из папки files, в виде ссылок, выглядит следующим образом:

<?php
include "d_conf.php";
include "d_func.php";

$db = connect();
$row = get_count($db);

if ($handle = opendir(DIR)) {

    while (false !== ($file = readdir($handle))) {
	
       if($file == "." || $file == "..") {
	   		continue;
	   }
	   $str = "<a href='download.php?file=$file'>$file</a>";
	   
	   if($row[$file]) {
	   
	   	$str .= "(".$row[$file].")";
	   }
	   $str .= "<br/>";
	   echo $str;
	   
    }

    closedir($handle);
}
?>

Как Вы видите, его код довольно простой. Вначале подключаем файл конфигурации и функций. Затем выполняем подключение к базе данных (функция connect()), далее в переменную $row сохраняем информацию о ранее скачиваемых файлах (функция get_count()), если конечно такие имеются. Далее обходим в цикле каждый элемент папки files, и если это файл – выводим его на экран в виде ссылки. Путь ссылки формируем следующим образом – вначале указываем имя файла обработчика (файл который будет выполнять подсчет скачивания), в нашем случае это файл download.php и используя метод GET (через адресную), передаем имя файла для скачивания.

Так как мы знаем, что ключи массива $row — это имена ранее скачиваемых файлов (мы с Вами условились, что в базу данных информация попадет после первого скачивания), поэтому, как только один из ключей данного массива совпадет с именем файла, при обходе папки files. Значит необходимо вывести на экран количество его скачивания пользователями. В этом случае, на экране мы увидим следующее:

Теперь создадим файл downlad.php и добавим следующий код:

<?php
include "d_conf.php";
include "d_func.php";

if($_GET['file']) {
		$file = $_GET['file'];
		update_file($file,$types);
		
}
?>

В данном файле необходимо проверить, существует ли в суперглобальном массиве $_GET, ячейка file, то есть имя файла для скачивания. Если такая ячейка существует, значит, вызываем функцию update_file($file,$types), передавая ей имя файла и массив допустимых типов файлов для скачивания.

Теперь давайте откроем файл d_funct.php и опишем функцию update_file(),которая должна подсчитать количество скачивания файла и отдать его пользователю для загрузки:

function update_file($file,$types) {
	if(!in_array(substr($file,-3),$types)) {
			exit("NOT Type");
		}
		
		if(!file_exists(DIR.$file)) {
			exit("NOT FILE");
		}
		$db = connect();
		$query = "INSERT INTO count1 (name, count) VALUES('$file', '1')ON DUPLICATE KEY UPDATE count=(count+1)";
		
		mysqli_query($db,$query);
		if(mysqli_error($db)) {
			exit('FAULT');
		}
		
		header("Location:".DIR.$file);
		exit();
}
?>

Итак, первым делом, проверяем, тип файла, то есть, определяем его расширение (используя функцию substr()), и используя функцию in_array, проверяем, есть ли в массиве допустимых типов ячейка с найденным расширением. Затем проверяем, существует ли в папке files запрашиваемый файл (данную проверку нужно выполнять, так как пользователь может вручную обратиться к файлу download.php и передать ему произвольный файл).

Далее выполняем соединение с базой данных и формируем SQL запрос для подсчета количества скачивания файла. Обратите внимание, какой мы используем запрос:

$query = "INSERT INTO count1 (name, count) VALUES('$file', '1') ON DUPLICATE KEY UPDATE count=(count+1)";

Первая часть SQL запроса (INTO count1 (name, count) VALUES(‘$file’, ’1′)) вставляет новые данные в таблицу count, а именно имя файла и количество скачивания 1 (данная часть работает для первого скачивания фала). Вторая часть (ON DUPLICATE KEY UPDATE count=(count+1)), срабатывает после дублирования данных в поле с индексом UNIQUE, то есть если в поле name при вставке данных будет дублироваться запись, будет выполнено обновление данных. А именно, имя файла остается прежним, обновляется только поле count – его текущее значение в таблице увеличиваем на единицу. Далее выполняем SQL запрос и если не было ошибок, отдаем файл пользователю на скачивание.

Теперь можно проверить работоспособность скрипта.

Вариант 2

Для второго варианта нам также потребуется таблица в базе данных. Поэтому создадим таблицу count2:

name – поле для хранения имени файла (тип данных String). Опять же данному полю необходимо присвоить индекс UNIQUE, так как имена файлов, должны быть уникальными;

id – идентификатор таблицы (тип данных Integer), а значит и идентификатор каждого добавленного файла (атрибут AUTO INCREMENT индекс PRIMARY KEY).

count – количество скачивания файла (тип данных Integer). Значение данного поля по умолчанию – 1.

SQL запрос для создания данной таблицы:

--
-- Структура таблицы `count2`
--

CREATE TABLE IF NOT EXISTS `count2` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `count` int(11) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

По логике работы данного скрипта, перед тем как вывести доступные файлы для скачивания, необходимо занести информацию о них в базу данных. Таким образом, каждому файлу будет присвоен собственный уникальный идентификатор, который будет использоваться для указания того, какой именно файл пользователь скачивает.

Как обычно нам необходим файл конфигурации, который мы используем из предыдущего варианта данного скрипта. Так же создадим файл d_func.php, в котором будут описаны функции необходимые для работы скрипта. В файле d_func.php создадим две функции:

function connect() {
	$db = mysqli_connect(HOST,USER,PASS,DB);
	if(mysqli_connect_error($db)) {
		exit(mysqli_connect_error($db));
	}
	return $db;
}

function insert_file($db,$file,$types) {
	
	if(empty($file)) {
		$_SESSION['fault'] = "Пустое имя файла";
		return FALSE;
	}
	
	if(!in_array(substr($file,-3),$types)) {
		$_SESSION['fault'] = "Не верный тип файла";
		return FALSE;
	}
	
	if(!file_exists(DIR.$file)) {
		$_SESSION['fault'] = "Данного файла нет";
		return FALSE;
	}
	
	$query = "INSERT IGNORE INTO count2 (name) VALUES('$file')";
	mysqli_query($db,$query);
	if(!mysqli_error($db)) {
		$_SESSION['fault'] = mysqli_error($db);
		return FALSE;
	}	
	return TRUE;
}

Первая функция connect() – взята из первого варианта скрипта. Интерес представляет вторая функция insert_file(), которая добавляет новый файл в базу данных. Аргументы, которые принимает функция: $db – дескриптор подключения к базе данных, $file – имя файла, $types – массив допустимых для скачивания файлов.

Перед тем как добавить информацию о файле в базу данных, выполним ряд проверок: не пустое ли имя файла, разрешается ли скачивание файла данного типа (аналогично первому варианту), а также существует ли указанный файл в папке files (константа DIR).

Затем формируем SQL запрос, по вставке данных в таблицу, при этом указываем ключевое слово IGNORE – то есть, отключаем формирование ошибок при вставке имени уже существующего файла. При этом вставка данных не произойдет. Далее выполняем запрос, и если не возникло ошибок, возвращаем истину.

Для получения информации о сохраненных файлах в базе данных, создадим функцию get_count(), логика ее работы аналогична первому варианту скрипта:

function get_count($db) {
	$query = "SELECT id,name,count FROM count2";
	$result = mysqli_query($db,$query);
	
	if(!$result) {
		return FALSE;
	}
	for($i = 0; $i < mysqli_num_rows($result);$i++) {
		$row[] = mysqli_fetch_assoc($result);
	}
	
	return $row;
}

Теперь давайте создадим файл admin.php, с помощью которого можно добавить новый файл в базу данных и просмотреть уже существующие:

<?php
session_start();

include "d_conf.php";
include "d_func.php";

$db = connect();
if($_SERVER['REQUEST_METHOD'] == 'POST') {
	$file = $_POST['file'];
	insert_file($db,$file,$types);
	header("Location:".$_SERVER['PHP_SELF']);
	exit();
}
$files = get_count($db);

?>

<!DOCTYPE HTML>
<html>
    <head>
		<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
		<title>Файлы</title>		
		<link rel="stylesheet" type="text/css" href="style.css" />			
	</head>
	<body>
		<div id="container">
			
			<?php if($_SESSION['fault']) :? >
				<?php echo $_SESSION['fault'];?>
				<?php unset($_SESSION['fault']);?>
			<? endif;?>
			
			<h2>Новый файл</h2>
			<form method="post">
				<input type="text" name="file"/>
				<input type="submit" value="ОК"/>
			</form>
			<?php if($files) :? >
			<h2>Файлы</h2>
			<table border="1px;">
				<thead>
					<th>id</th>
					<th>Имя</th>
					<th>Скачали</th>
					<th>Ссылка</th>
				</thead>
				<tbody>
				<?php foreach($files as $item) :? >
					<?php if(file_exists(DIR.$item['name'])) :? >
						<tr style="color:green">
					<?php else: ?>
						<tr style="color:red">
					<?php endif;?>
					
						<td><?php echo $item['id'];?></td>
						<td><?php echo $item['name'];?></td>
						<td><?php echo $item['count'];?></td>
						<td>download.php?id=<?php echo $item['id'];?></td>
					</tr>
				<?php endforeach;?>
				</tbody>
			</table>	
			<?php endif;?>
			
		</div>
	</body>
</html>

Как обычно вначале подключаем требуемые файлы и выполняем подключение к базе данных. Затем так как мы добавляем новый файл, используя обычную форму, значит как только придут данные методом POST – вызовем функцию insert_file(), то есть добавим новый файл в базу данных. Далее выполняем перенаправление, на эту же страницу, для очистки данных в суперглобальном массиве $_POST, и вызываем функцию get_count(), что бы просмотреть, какие файлы уже добавлены в базу данных и сколько раз их скачивали. Далее выводим файлы на экран в нужном формате, единственное, я добавил проверку наличия файла при его выводе на экран. Это нужно для того что бы, файлы которые действительно присутствуют в папке files – отображать зеленным цветом, в противном случае – красным.

Вот, что мы увидим на экране браузера, после добавления нескольких файлах.

Теперь давайте создадим страницу, на которой выведем ссылки для скачивания файлов (файл index.php):

<?php
include "d_conf.php";
include "d_func.php";

$db = connect();
$files = get_count($db);
?>

<!DOCTYPE HTML>
<html>
    <head>
		<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
		<title>Файлы</title>		
		<link rel="stylesheet" type="text/css" href="style.css" />			
	</head>
	<body>
		<div id="container">
			<h2>Файлы</h2>
				<?php foreach($files as $item) :? >
					<a href="download.php?id=<?php echo $item['id'];?>"><?php echo $item['name']?>(<?php echo $item['count']?>)</a><br />
				<?php endforeach;?>	
		</div>
	</body>
</html>

Как обычно путь ссылки формируем следующим образом – вначале указываем файл обработчик (который выполнит подсчет количества скачивания), затем передаем, используя GET параметры, идентификатор файла, который пытается скачать пользователь. Теперь создадим файл download.php:

<?php
include "d_conf.php";
include "d_func.php";


if(!$_GET['id']) {
	exit ("Не правильный URL");
}

$id = (int)$_GET['id'];
if(!$id) {
	exit ("Не правильный идентификатор");
}

$db = connect();
update_count($db,$id);

?>

Обратите внимание, логика работы данного файла полностью аналогична первому варианту. За исключением того, что через адресную строку передается идентификатор скачиваемого файла, а не его имя. Так как идентификатор – это число, значит проверим, является ли содержимое ячейки id суперглобального массива $_GET, числом. И если действительно это так – вызываем функцию update_count(). Поэтому в файле d_func.php опишем данную функцию:

function update_count($db,$id) {
	$query = "SELECT count,name FROM count2 WHERE id='$id'";
	$result = mysqli_query($db,$query);
	$res = mysqli_fetch_row($result);
	
	if(count($res) == 0) {
		exit("Нет файла в базе");
	}
	if(!file_exists(DIR.$res[1])) {
		exit('Такого файла нет');
	}
	
	$count_f = $res[0]+1;
	
	$sql = "UPDATE count2 SET count='$count_f' WHERE id='$id'";
	$result = mysqli_query($db,$sql);
	if(mysqli_error($db)) {
		exit(mysqli_error($db));
	}
	if(mysqli_affected_rows($db) != 1) {
		exit('Ошибка');
	}
	
	header("Location:".DIR.$res[1]);
	exit();
}

Данная функция принимает в качестве параметров переменную $db – дескриптор подключения к базе данных и переменную $id – идентификатор скачиваемого файла. Первым делом необходимо убедиться, что файл с переданным идентификатором действительно существует в базе данных. При этом получим его имя. Далее проверяем наличие файла с определенным именем в папке загрузок – files. И если данный файл действительно существует – обновляем запись в таблице. А именно увеличиваем текущее значение поля count на единицу. Далее, используя функцию header(), отдаем файл пользователю на скачивание.

Давайте посмотрим, что у нас получилось:

Как Вы видите, все успешно работает. На этом данный урок завершен. Всего Вам доброго и удачного кодирования!!!

Курс по программированию на языке PHP

Изучите PHP с нуля до результата!

Смотреть курс

Метки:

Комментарии Вконтакте:

Комментарии Facebook:

Комментарии (12)

  1. Alex

    Это конечно хорошо что вы так старательно пытаетесь все закрыть лишь бы народ подписался, когда в интернете столько информации, могли бы просто предложить а не насильно все закрыть.. Но давайте не об этом.

    По теме, сохранить папку директории без полного пути разве это правильно ? А что если я поставлю весь этот код в не в корне сайта, а папку для скачивания в корне ?

    Пройтись по полученным записям через фор… не проще через while ?
    Про sql инъекции я смотрю вы вообще забыли, раз не фильтрующие данные.

    • Виктор Гавриленко

      Здравствуйте!
      Конечно, использование «не полного пути это не правильно», но в уроке я показал — как можно создать счетчик загрузки файлов в двух вариантах. Цель урока — не дать готовое решение (что бы его можно было просто вставить в сайт), а показать как создаются подобные счетчики.
      Защиту от SQL иньекций я не включал урок, что бы код был более компактным и реализовывал только поставленную задачу, к тому же — это не тема урока. Хотя безопасность при разработке скрипта в целом должна быть на первом месте! И если создавать полный законченный проект, то конечно данную защиту обязательно нужно обязательно предусмотреть. Но обратите внимание, что через адресную строку мы передаем имя файла — для скачивания(в первом варианте) и идентификатор файла во втором варианте. Если человек вместо имени файла напишет произвольную SQL иньекцию, то это уже не будет именем файла и проверка в коде скрипта (на наличие файла в папке) уже не пройдет — при этом будет выполнен выход из приложения. Ну а по второму варианту — идентификатор, который передается, через адресную строку — приводится к целочисленному типу данных. И конечно же останется только число.
      Но в любом случае — это правильно — что обратили внимание на вопросы безопасности.

  2. Андрей

    В первом варианте счетчика выбивает ошибку не могу понять почему. Счетчик считает а файлы не скачиваются.
    Warning: Cannot modify header information — headers already sent by (output started at Z:\home\a\www\download.php:1) in Z:\home\a\www\d_func.php on line 40

      • Андрей

        Спасибо! Проблема была в кодировки. Только вот место скачивания начинается открытия файла.

      • Alexey

        А как можно вывести на экран значение поля name
        Мне это надо чтобы удалять не только строку из таблицы, но и удалять файл с сервера.
        Можете изменить функцию.
        Заранее спасибо.

        Внимание!!!
        Кто возьмет функцию не забудьте поправить кавычки.

        функция del

        function del($id){
        $db = connect();
        $sql = «DELETE FROM `count2` WHERE id=$id»;
        mysqli_query($db,$sql);
        header(«Location: admin.php»);
        return true;
        exit();
        }

        файл del.php

  3. Сергей

    Здравствуйте.Как быть если файлы скачиваю с удаленного сервера files.mysite.ru/download.rar т.к.на хостинге где находится сайт нет места ?

    • Алексей Степанов

      Здравствуйте!!!
      Это можно сделать простым способом в файле d_conf.php
      Изменяешь строку:
      define("DIR","files/");
      на
      define("DIR","http://files.mysite.ru/");

  4. Alexey

    Уже все разобрался, Помощи не надо.

  5. Alexey

    Кому нужен счетчик скачивания файлов с закрытой админкой, встроенной загрузкой файлов путем перетаскивания файлы будут записываться в базу данных сразу же после загрузки их на сервер и удалением файлов с сервера и из балы данных, пишите мне в Вк

  6. Fira

    Спасибо за отличную статью, ваш урок мне очень помог.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Я не робот.

Spam Protection by WP-SpamFree