8768 sujets

Développement web côté serveur, CMS

Salut,

Pour des fichiers à télécharger (et non simplement afficher), j'ai besoin à la fois de forcer l'apparition de la boite de dialogue "Enregistrer sous..." et de mettre un compteur de téléchargements.

Je me suis inspiré du code décrit dans ce post, mais mon problème est que ça ne fonctionne que si les fichiers sont à la racine du site.

Un problème de droits ? Cela semblerais logique, mais les images qui sont dans un sous-dossier s'affichent bien, mais si j'y mets mes fichiers à télécharger, on ne peut pas y avoir accès... Quel droits mettre, ou quelle est la raison du problème ?

Je suis sous OpenBSD avec Nginx et peu familier avec cet environnement, mais bon, ça ne devrait quand même pas changer les choses fondamentalement ?
Salut !

Tu as pas un message d'erreur à partager ou un bout de code ? Sans quoi c'est un peu difficile de trouver le problème. Tu es sûr de fournir le bon chemin (relatif au script qui gère le téléchargement) vers le fichier ?
Modifié par Anymah (26 Apr 2018 - 18:57)
Anymah a écrit :
Tu as pas un message d'erreur à partager ou un bout de code ?

Le problème, c'est justement que je n'ai pas de message d'erreur. J'essaie de trouver pourquoi je n'ai aucun log PHP ni même HTTP... Mon php.ini semble pourtant avoir tout ce qu'il faut Smiley hein

Pour le code, j'aurais peut-être dû le mettre, mais il n'y a pas grand chose de changé par rapport à celui du post que j'ai indiqué. Et comme ça fonctionne quand les fichiers sont dans la racine, je doute un peu que ça vienne de là... Mais bon, le voici :
<?php
// Téléchargement de fichiers avec compteur

// désactive le temps max d'exécution
set_time_limit(0);

// démarrage de la session
session_start();

if (empty($_GET["file"])) {
    header("HTTP/1.1 404 Not Found");
    exit;
}

if(!preg_match('`^[^\\:*?"<>|]*$`', $fichier)) {
  exit('Nom fichier incorrect.');
}

$fichier = pathinfo($_GET["file"]);

if($fichier['extension'] == ".htm" || $fichier['extension'] == ".html" || $fichier['extension'] == ".php" ) {
    header("HTTP/1.1 400 Bad Request");
    exit;
}
$filename = $_GET["file"];

// Compteur de visites
//$monfichier = fopen('data/media/telechargements/'.$fichier['basename'].'.txt', 'r+');
$monfichier = fopen($fichier['basename'].'.txt', 'r+');

$visites = fgets($monfichier);
$visites = $visites + 1; // $visites++ ne fonctionne pas !?

fseek($monfichier, 0);
fputs($monfichier, $visites);
fclose($monfichier);

$size = filesize($filename);

// Evite erreurs selon navigateur si compression activée
if (ini_get("zlib.output_compression")) {
    ini_set("zlib.output_compression", "Off");
}

// Permet de continuer à naviguer pendant le téléchargement
session_write_close();

header("Cache-Control: no-cache, must-revalidate");
header("Cache-Control: post-check=0,pre-check=0");
header("Cache-Control: max-age=0");
header("Pragma: no-cache");
header("Expires: 0");
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename="'.$filename.'"');

// indique la taille du fichier à télécharger
header("Content-Length: ".$size);

// envoie le contenu du fichier
readbigfile($filename);

// Lecture de gros fichiers
function readbigfile($file) {
		// bytes per chunk, using 3mb
		$chunksize = 3*1024*1024;

		// open the file in binary mode
		if(($handle = fopen($file, 'rb')) === false) {
			return false;
		}
		// read it
		else do {
			if(($chunk = fread($handle, $chunksize)) === false)
				return false;	// an error occured
			// display
			echo $chunk;
			// free memory
			unset($chunk);
		} while(feof($handle) === false);

		// close the file
		if(fclose($handle) === true)  {
			return true;
		}
		else {
			return false;
		}
}
?>


Anymah a écrit :
Tu es sûr de fournir le bon chemin (relatif au script qui gère le téléchargement) vers le fichier ?

Ben oui, je l'ai même fait afficher après tests et découpage pour être sûr ! D'ailleurs, même le compteur de visites ne fonctionne pas si son fichier texte n'est pas dans la racine. J'ai laissé la ligne avec le chemin relatif "normal" en remarque, sachant que le script PHP est dans la racine.
Tu devrais utiliser la constante __DIR__ pour être certain que tu vas chercher le fichier dans le bon dossier
http://php.net/manual/fr/language.constants.predefined.php
et utiliser file_exists() pour vérifier le chemin du fichier. Envoyer une 404 en cas d'échec
http://php.net/manual/fr/function.file-exists.php

Utilise plutôt :
//  http://php.net/manual/fr/filter.filters.sanitize.php
 
$filename = filter_input(INPUT_GET, 'file', FILTER_SANITIZE_STRING);
if(empty($filename)) {
   header("HTTP/1.1 400 Bad Request");
   exit;
}
$filename = __DIR__.'/'.$filename;
if(
   !file_exists($filename) or
   !in_array(
       path_info($filename, PATHINFO_EXTENSION),
       explode(' ', 'php html htm')
    )
) { 
  header("HTTP/1.1 404 File not found");  
  exit;
}
// Envoie le fichier

Modifié par bazooka07 (27 Apr 2018 - 15:41)
Salut,

Tiens, tu es ici aussi, bazzoka07 ! Merci pour ta réponse !

Effectivement, je n'avais pas pensé à utiliser la constante magique __DIR__, mais malheureusement ça ne solutionne pas le problème.

Pour le file_exits, je l'avais mis et l'ai supprimé, parce qu'évidemment le retour est négatif. Il faut vraiment que je trouve comment mettre les logs en route, un petit message d'erreur serait le bienvenu pour savoir que chercher!
Une solution basique pour déboguer est de cacher des commentaires dans la page HTML générée :
$filename = filter_input(INPUT_GET, 'file', FILTER_SANITIZE_STRING);
if(empty($filename)) {
   header("HTTP/1.1 400 Bad Request");
   exit;
}
$filename = __DIR__.'/'.$filename;
echo "<!--\n";
print_r($_GET);
echo "\$filename: $filename\n";
echo "-->\n";
if(
   !file_exists($filename) or
   !in_array(
       path_info($filename, PATHINFO_EXTENSION),
       explode(' ', 'php html htm')
    )
) { 
  header("HTTP/1.1 404 File not found");  
  exit;
}

Et après on affiche le code source de la page dans le navigateur.

Un peu plus sophistiqué error_log()
http://php.net/manual/fr/function.error-log.php
avec $message_type="3" pour envoyer le message d'un fichier perso;

Et le must : xdebug
Salut,

Désolé pour le temps de réaction... J'ai été très pris, entre autres par l'arrivée de la fibre optique Smiley smile .

Oui, j'avais fait afficher des commentaires et messages d'erreur, mais ça ne m'a pas permis d'avancer, juste de confirmer que l'adressage du fichier est bon, et que PHP ne peut pas l'ouvrir, certainement parce qu'il ne le "voit" pas, d'après le retour de file_exists().

Sauf si j'ai mal compris quelque chose, error_log() ne m'apportera rien de plus, c'est juste une question pratique selon où et comment on veut que les messages s'affichent.

Donc, il ne me reste plus qu'à installer xdebug... Mais avant, je veux quand même essayer de faire fonctionner les logs PHP : généralement, quand j'ai des problèmes, ils me permettent d'en trouver rapidement l'origine. Ça ne devrait être qu'un détail oublié dans php.ini...