Compteur de téléchargements en PHP MySQL

A quoi sert un compteur de téléchargements ?

Ce compteur est un script simple qui va vous permettre de savoir combien de fois a été téléchargé un fichier. Cela sert par exemple pour voir l'impact d'une page (ou d'un outil) sur un site, mais cela peut aussi servir à compter combien de gens ont ouvert votre newsletter ... En réalité, son principe de fonctionnement est très simple. Nous allons créer une page qui aura 2 rôles :

  1. A l'appel d'un fichier, incrémenter le compteur (c.à.d. ajouter "+1" dans la base de données)
  2. Bien entendu, délivrer le fichier au visiteur

Pour réaliser ce compteur, nous aurons besoin de 2 langages puisque tout le gros de l'action se déroulera côté serveur : PHP et MySQL.

Pourquoi MySQL et PHP ?

La base de données, base MySQL, est située sur le serveur. Le visiteur sur votre site ne la verra pas. Il n'y a pas accès (en principe ^^) et le seul moyen d'incrémenter les données de cette base est bien de passer par un langage qui sait interroger et manipuler des données : un langage serveur. Certes le choix peut être plutôt vaste, le PHP est totalement arbitraire, d'abord parce que c'est un vieil ami et ensuite parce que la plupart des hébergeurs le proposent.

Ceci dit, si votre serveur n'est pas sous Apache mais Microsoft, le principe reste le même entre ASP et SQL Server par exemple. Seule la formulation du langage change.

Bref, afin de centraliser les données (pour que tous les visiteurs manipulent bien les mêmes informations, et non chacun les leur sur leur ordinateur), il faut les centraliser, d'où l'intérêt indéniable de les mettre sur le serveur qui abrite le site.

Code source du compteur de téléchargements en PHP MySQL

La table dans la base MySQL

Pour ce faire, nous allons créer plusieurs éléments. Tout d'abord, la base de données. Je vous propose ici une structure générique, truffée de commentaires, si vous souhaitez y ajouter d'autres informations, vous pouvez tout à fait (le chemin du fichier par exemple)

-- Création de la table : 'downloads_files'

CREATE TABLE downloads_files (
	id         INT(10)      auto_increment, -- le numéro du fichier en base de données

	filename   VARCHAR(255) NOT NULL, -- le nom du fichier
	downloaded INT(5)       NOT NULL DEFAULT 0, -- par défaut, téléchargé 0 fois

  PRIMARY KEY(id),
  UNIQUE(filename) -- je suppose que chaque fichier est unique ;o)
) Engine = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;


-- On crée les enregistrements correspondant aux fichiers mis à disposition sur votre site
-- vous pouvez en créer autant que vous voulez
INSERT INTO downloads_files(filename) VALUES('programme_de_la_soiree.pdf');
INSERT INTO downloads_files(filename) VALUES('livret_accueil.zip');

-- vous remarquerez que nous ne renseignons ni ID (qui est automatiquement calculé)
-- ni le champ "downloaded" qui vaut 0 par défaut.

La structure de cette table est assez simple, je supposerai dans ce tuto que tous les fichiers sont dans un même dous dossier. Sinon, il suffit de rajouter un champ dans la table ci-dessus, par exemple filedir VARCHAR(255) NOT NULL,

Le code source PHP de download.php

download.php sera notre unique fichier qui centralisera tous les téléchargements. En réalité, c'est via un paramètre qu'on appellera le fichier, download.php se chargeant alors d'incrémenter le compteur et de délivrer le fichier au visiteur.

Voici son code source :

<?php
	// Mini config
	$MYSQL_HOST = 'localhost';
	$MYSQL_DB = 'ma_base';
	$MYSQL_USR = 'utilisateur';
	$MYSQL_PWD = 'palmier';
	$filesdir = '../documents/fichiers/'; // le chemin relatif où sont stockés les fichiers (si pas mis dans la base)

	// Connexion MySQL
	try
	{
		$bdd = new PDO('mysql:host='.$MYSQL_HOST.';dbname='.$MYSQL_DB.';charset=utf8', $MYSQL_USR, $MYSQL_PWD);
	}
	catch (Exception $e)
	{
		die('Erreur : ' . $e->getMessage());
	}

	// Récupération du fichier passé en paramètre
	$id_fichier = (isset($_GET['f'])) ? trim(intval(sprintf("%d", $_GET['f']))) : 0;

	// est-ce que ce fichier existe ?
	$req_testFichierExiste = $bdd->prepare("SELECT id, filename, downloaded FROM downloads_files WHERE id= :idFichier LIMIT 1");
	$req_testFichierExiste->execute(array('idFichier' => $id_fichier));

	$fichier_existe = false; $fichier = array();
	while ($test = $req_testFichierExiste->fetch())
	{
		if ((isset($test['filename'])) && ($test['filename'] != ''))
		{
			$fichier_existe = true;
			$fichier = $test;
			break;
		}
	}
	$req_testFichierExiste->closeCursor();

	if ($fichier_existe == false)
		die('Fichier non trouvé.');


	// ici nous sommes sûrs que le fichier n°XXX existe bien en base de données
	// IMPORTANT : avant quoi que ce soit, on vérifie que le fichier existe aussi sur le serveur
	// (et pas seulement en base de données)
	if ((file_exists($filesdir . $fichier['filename'])) && (is_file($filesdir . $fichier['filename'])))
	{
		// Le fichier existe bien : on va incrémenter son compteur de téléchargements
		$req_augmenterTelechargements = $bdd->prepare("UPDATE downloads_files SET downloaded = (downloaded+1) WHERE id= :idFichier");
		$req_augmenterTelechargements->execute(array(':idFichier' => $id_fichier));

		// requête effectuée : on envoie le fichier
		header('Content-Description: File Transfer');
		header('Content-Type: application/octet-stream');
		header('Content-Disposition: attachment; filename="'.$fichier['filename'].'"');
		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($filesdir . $fichier['filename'])); //Absolute URL
		ob_clean();
		flush();
		readfile($filesdir . $fichier['filename']); //Absolute URL
		exit();
	}
?>

Mise en ligne et comptage : page "accueil.html"

Et maintenant, le code sur le site Web dans la page (arbitrairement) nommée accueil.html : (ce code peut être mis n'importe où, dans de l'HTML, dans du Javascript, dans du PHP, peu importe : le tout est d'appeler download.php?f=ID_DE_VOTRE_FICHIER), il faut placer les liens vers nos fichiers à télécharger :

<h2>Zone téléchargements</h2>

	<h3>Votre soirée</h3>
	<p><a href="download.php?f=1">Télécharger le programme de la soirée.</a></p>

	<h3>Livret d'accueil</h3>
	<p><a href="download.php?f=2">Télécharger le livret d'accueil.</a></p>

Pour ces 2 fichiers, il faudra bien entendu créer dans la table une entrée "1" => "programme_de_la_soiree.pdf" et une entrée "2" => livret_accueil.zip" par exemple (comme créé plus haut).

On peut imaginer plein d'améliorations, comme par exemple la date du dernier téléchargement, un comptage par mois, semaines, etc., toutes ces idées sont plus ou moins faciles à réaliser ...

Quelques fonctionnalités avancées

Ici, nous allons afficher la date de dernier téléchargement sur le site. De plus, nous afficherons le nombre total de téléchargements pour chaque lien/fichier.

Pour ce faire, nous allons réaliser une petite fonction PHP, laquelle permettra de rapatrier facilement ces données. La page affichant le lien download.php?f=ID_DE_VOTRE_FICHIER (accueil.html plus haut) doit, pour cette dernière partie, obligatoirement comporter l'extension .php (nous la renommerons donc accueil.php).

La table MySQL correspondante comporte un champ de plus, pour stocker la date de dernier téléchargement :

-- Création de la table : 'downloads_files'

CREATE TABLE downloads_files (
	id            INT(10)      auto_increment, -- le numéro du fichier en base de données

	filename      VARCHAR(255) NOT NULL, -- le nom du fichier
	downloaded    INT(5)       NOT NULL DEFAULT 0, -- par défaut, téléchargé 0 fois
	last_download DATETIME     NOT NULL DEFAULT '0000-00-00 00:00:00',

	PRIMARY KEY(id),
	UNIQUE(filename) -- je suppose que chaque fichier est unique ;o)
) Engine = MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;



-- vous remarquerez que nous ne renseignons ni ID (qui est automatiquement calculé)
-- ni le champ "downloaded" qui vaut 0 par défaut.
-- ni la date par défaut qui vaut 0000-00-00 00:00:00 aussi

La page download.php reste quasi la même que précédemment. On rajoute juste la date de téléchargement :

<?php
	// Mini config
	$MYSQL_HOST = 'localhost';
	$MYSQL_DB = 'ma_base';
	$MYSQL_USR = 'utilisateur';
	$MYSQL_PWD = 'palmier';
	$filesdir = '../documents/fichiers/'; // le chemin relatif où sont stockés les fichiers (si pas mis dans la base)

	// Connexion MySQL
	try
	{
		$bdd = new PDO('mysql:host='.$MYSQL_HOST.';dbname='.$MYSQL_DB.';charset=utf8', $MYSQL_USR, $MYSQL_PWD);
	}
	catch (Exception $e)
	{
		die('Erreur : ' . $e->getMessage());
	}

	// Récupération du fichier passé en paramètre
	$id_fichier = (isset($_GET['f'])) ? trim(intval(sprintf("%d", $_GET['f']))) : 0;

	// est-ce que ce fichier existe ?
	$req_testFichierExiste = $bdd->prepare("SELECT id, filename, downloaded FROM downloads_files WHERE id= :idFichier LIMIT 1");
	$req_testFichierExiste->execute(array(':idFichier' => $id_fichier));

	$fichier_existe = false; $fichier = array();
	while ($test = $req_testFichierExiste->fetch())
	{
		if ((isset($test['filename'])) && ($test['filename'] != ''))
		{
			$fichier_existe = true;
			$fichier = $test;
			break;
		}
	}
	$req_testFichierExiste->closeCursor();

	if ($fichier_existe == false)
		die('Fichier non trouvé.');


	// ici nous sommes sûrs que le fichier n°XXX existe bien en base de données
	// IMPORTANT : avant quoi que ce soit, on vérifie que le fichier existe aussi sur le serveur
	// (et pas seulement en base de données)
	if ((file_exists($filesdir . $fichier['filename'])) && (is_file($filesdir . $fichier['filename'])))
	{
		// Le fichier existe bien : on va incrémenter son compteur de téléchargements
		$req_augmenterTelechargements = $bdd->prepare("UPDATE downloads_files SET downloaded = (downloaded+1), last_download= :date_telechargement WHERE id= :idFichier");
		$req_augmenterTelechargements->execute(array(':date_telechargement' => date('Y-m-d H:i:s'), ':idFichier' => $id_fichier));

		// requête effectuée : on envoie le fichier
		header('Content-Description: File Transfer');
		header('Content-Type: application/octet-stream');
		header('Content-Disposition: attachment; filename="'.$fichier['filename'].'"');
		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($filesdir . $fichier['filename'])); //Absolute URL
		ob_clean();
		flush();
		readfile($filesdir . $fichier['filename']); //Absolute URL
		exit();
	};
?>

Voilà, notre fonction et les stats sont prêtes. Dans cette page accueil.php, on peut donc afficher quelques statistiques sur les liens.

Tout en haut de votre page accueil.php, mettez ce code :

<?php
	// Mini config
	$MYSQL_HOST = 'localhost';
	$MYSQL_DB = 'ma_base';
	$MYSQL_USR = 'utilisateur';
	$MYSQL_PWD = 'palmier';

	// Connexion MySQL
	try
	{
		$bdd = new PDO('mysql:host='.$MYSQL_HOST.';dbname='.$MYSQL_DB.';charset=utf8', $MYSQL_USR, $MYSQL_PWD);
	}
	catch (Exception $e)
	{
		die('Erreur : ' . $e->getMessage());
	}



	function AfficheDetailsTelechargements($id_fichier)
	{
		global $bdd;

		// est-ce que ce fichier existe ?
		$req_testFichierExiste = $bdd->prepare("SELECT id, filename, downloaded, last_download FROM downloads_files WHERE id= :idFichier LIMIT 1");
		$req_testFichierExiste->execute(array(':idFichier' => $id_fichier));

		$fichier_existe = false; $fichier = array();
		while ($test = $req_testFichierExiste->fetch())
		{
			if ((isset($test['filename'])) && ($test['filename'] != ''))
			{
				$fichier_existe = true;
				$fichier = $test;
				break;
			}
		}
		$req_testFichierExiste->closeCursor();

		if ($fichier_existe == false)
			return array(); // fichier non existant, on retourne un tableau vide
		else
			return $fichier; // fichier existant, on retourne le tableau des infos du fichier
	};
?>

Et le bloc de liens devient donc :

<h2>Zone téléchargements</h2>

	<?php $f1 = AfficheDetailsTelechargements(1); ?>
	<h3>Votre soirée</h3>
	<p><a href="download.php?f=1">Télécharger le programme de la soirée.</a> <em>(<?php echo $f1['downloaded'].' téléchargement(s), dernier téléchargement le '.$f1['last_download']; ?>)</em></p>

	<?php $f2 = AfficheDetailsTelechargements(2); ?>
	<h3>Livret d'accueil</h3>
	<p><a href="download.php?f=2">Télécharger le livret d'accueil.</a> <em>(<?php echo $f2['downloaded'].' téléchargement(s), dernier téléchargement le '.$f2['last_download']; ?>)</em></p>