Discussion:
Script de telechargement qui ne tele charge pas tout !
(trop ancien pour répondre)
Vincent Verdon
2010-08-15 09:31:34 UTC
Permalink
Bonjour,

plutôt débutant en php (ma langue naturelle est le TCL ;-) ), je bute
sur un problème gênant :
j'ai créé un ensemble (modeste) de scripts dont le but est de gérer
l'accès à des répertoires sur un serveur web. Il s'agit en fait d'une
zone de téléchargement qui n'est pas en accès libre mais nécessite
d'être authentifié au préalable. Cette authentification est faite en
amont depuis un CMS (en l'occurence SPIP). Le pb est que certaines
personnes connectées ne perviennet pas à télécharger l'intégralité des
fichiers qu'elles souhaitent : les fichier se trouvent ainsi corrompus.
Je ne comprends pas pourquoi, et je n'ai jamais eu personnellement ce
problème quand je fais des essais ! Je suppose donc plusieurs choses :
1) C'est lié à l'OS de l'utilisateur (perso, j'utilise Linux) ;
2) C'est lié au fournisseur d'accès : le script est sur un site hébergé
chez Free et je suis moi-même chez Free.
3) Je programme bien mal, ce qui est fort possible.

Voici le morceau de script qui provoque le téléchargement. Vous
constaterez que j'ai essayé 2 fonctions différentes fpassthru et
readfile, mais cela donne le même résultat... Au passsage, j'ai
l'impression qu'en PHP il y a beaucoup de fonctions différentes qui
permettent d'arriver au résultat voulu : ça va être long à apprendre
tout ça !

--------------------
// on récupère les infos sur le fichier et on crée l'entête
$taille = filesize($fic);
$type_mime = def_mime_type($fic);
$f_nom = basename($fic);

//on ecrit l'entete
header('Content-Type: '.$type_mime);
header('Content-Length: '.$taille);
header('Content-Disposition: attachment; filename='.$f_nom);
header('Content-Transfer-Encoding: binary');

// on ouvre le fichier en lecture en mode binaire
//$file = @ fopen($fic, 'rb');
//if ($file) {
// on envoie le fichier
// fpassthru($file);
// on ecrit le log d'accès
// ecrire_log("acces au fichier $_GET[fic]");
//} else {
// echo "Il y a eu une erreur de chargement" ;
// ecrire_log("echec de l'acces au fichier $_GET[fic]");
//}

if(readfile("$fic")) {
ecrire_log("acces au fichier $_GET[fic]");
} else {
ecrire_log("echec de l'acces au fichier $_GET[fic]");
}
--------------------


Merci de votre aide
--
Amicalement, Vincent Verdon
Antoine Polatouche
2010-08-16 12:40:42 UTC
Permalink
Post by Vincent Verdon
Le pb est que certaines
personnes connectées ne perviennet pas à télécharger l'intégralité des
fichiers qu'elles souhaitent : les fichier se trouvent ainsi corrompus.
Je ne comprends pas pourquoi, et je n'ai jamais eu personnellement ce
1) C'est lié à l'OS de l'utilisateur (perso, j'utilise Linux) ;
2) C'est lié au fournisseur d'accès : le script est sur un site hébergé
chez Free et je suis moi-même chez Free.
3) Je programme bien mal, ce qui est fort possible.
Une hypothèse à ajouter:
4) Le script php se termine sur un timeout et interrompt le téléchargement.
Yop
2010-08-16 17:04:48 UTC
Permalink
Post by Vincent Verdon
Je ne comprends pas pourquoi, et je n'ai jamais eu personnellement ce
problème quand je fais des essais !
modifier php.ini (sous debian à /usr/local/lib/php5)
par exemple :
max_execution_time = 90 au lieu de 30
memory_limit = 32M au lieu de 8M
upload_max_filesize = 64M au lieu de 2M

optimiser les valeurs en fonction des besoins.

Y
John GALLET
2010-08-16 17:44:47 UTC
Permalink
Bonjour,

Je répond uniquement en fonction des besoins de la question d'origine,
pour autant qu'on puisse changer ces valeurs chez free.fr, mais les
valeurs proposées sont tout à fait cohérentes dans le cadre général.
Post by Yop
max_execution_time = 90 au lieu de 30
Ce sont des secondes CPU, donc là peu probable que ça change grand chose.
Post by Yop
memory_limit = 32M au lieu de 8M
Mange pas de pain, mais chez free j'ai un doute de la possibilité.
Post by Yop
upload_max_filesize = 64M au lieu de 2M
Pour du download, totalement inopérant.

a++;
JGA
Dominique Ottello
2010-08-18 09:17:29 UTC
Permalink
Post by John GALLET
Post by Yop
memory_limit = 32M au lieu de 8M
Mange pas de pain, mais chez free j'ai un doute de la possibilité.
Chez free, c'est déjà à 32M. Suffit de faire un phpinfo() :
memory_limit 32M 32M
Vincent Verdon
2010-08-25 04:26:42 UTC
Permalink
Bonsoir,
Post by Yop
Post by Vincent Verdon
Je ne comprends pas pourquoi, et je n'ai jamais eu personnellement ce
problème quand je fais des essais !
modifier php.ini (sous debian à /usr/local/lib/php5)
max_execution_time = 90 au lieu de 30
memory_limit = 32M au lieu de 8M
upload_max_filesize = 64M au lieu de 2M
optimiser les valeurs en fonction des besoins.
Y
Bien entendu, le serveur étant chez Free, je n'ai pas la main sur la
config !
--
Amicalement, Vincent Verdon
John GALLET
2010-08-16 17:44:47 UTC
Permalink
Bonjour,
Post by Vincent Verdon
amont depuis un CMS (en l'occurence SPIP). Le pb est que certaines
personnes connectées ne perviennet pas à télécharger l'intégralité des
fichiers qu'elles souhaitent : les fichier se trouvent ainsi corrompus.
Chez moi, spip et le safe mode font mauvais ménage. Bien vérifier qu'il
n'est pas activé.

Remarque: free.fr a pendant longtemps eu des versions de PHP "tripotées"
à leur sauce, avec parfois des comportements peu intuitifs, les anciens
savent de quoi je parle avec mail()... Aujourd'hui, je ne sais pas.
Post by Vincent Verdon
Voici le morceau de script qui provoque le téléchargement. Vous
constaterez que j'ai essayé 2 fonctions différentes fpassthru et
readfile, mais cela donne le même résultat...
C'est normal, sauf boucle infinie, du moment que la section qui envoie
les données est correcte, le fonctionnement dépend de l'habillage.
Post by Vincent Verdon
Au passsage, j'ai l'impression qu'en PHP il y a beaucoup de fonctions différentes qui
permettent d'arriver au résultat voulu : ça va être long à apprendre
tout ça !
L'API est en effet très riche, avec des choses très utiles ou des
aberrations caractérisées (par exemple la fonction qui donne l'heure de
lever et coucher du soleil selon les coordonnées LAT/LONG, je pense
qu'elle aurait pu aller dans PEAR ou PECL...)
Post by Vincent Verdon
--------------------
// on récupère les infos sur le fichier et on crée l'entête
Oui mais non: si les headers sont déjà partis on est morts. Donc un
script qui fait du download doit nécessairement:
1) commencer en PREMIERE LIGNE et SANS caractère quel qu'il soit avant
<?php par :
<?php
ob_start();

Variante: activer ceci par défaut dans php.ini ou par htaccess.

Ensuite avant de construire le contenu du résultat, on vire tout ce qui
a pu aller dans le buffer par un :
ob_clean();
Post by Vincent Verdon
$taille = filesize($fic);
$type_mime = def_mime_type($fic);
$f_nom = basename($fic);
//on ecrit l'entete
Perso je le fais après traitement des éventuelles erreurs mais peu
importe, ça doit fonctionner.
Post by Vincent Verdon
header('Content-Type: '.$type_mime);
header('Content-Length: '.$taille);
header('Content-Disposition: attachment; filename='.$f_nom);
header('Content-Transfer-Encoding: binary');
Perso je n'ai jamais utilisé que deux headers: le content type par
exemple ci-dessous pour un csv, et le disposition à l'identique.

header("Content-Type: text/csv; charset=iso-8859-1;");
header("Content-disposition: attachment; filename=$file_name");
Post by Vincent Verdon
// on ouvre le fichier en lecture en mode binaire
//if ($file) {
// on envoie le fichier
// fpassthru($file);
Penser à fermer le $file. On peut aussi utiliser fileexists() au lieu du
fopen() si on ne s'en sert pas derrière comme avec readfile().
Post by Vincent Verdon
// on ecrit le log d'accès
// ecrire_log("acces au fichier $_GET[fic]");
//} else {
// echo "Il y a eu une erreur de chargement" ;
// ecrire_log("echec de l'acces au fichier $_GET[fic]");
//}
if(readfile("$fic")) {
ecrire_log("acces au fichier $_GET[fic]");
} else {
ecrire_log("echec de l'acces au fichier $_GET[fic]");
}
Du moment que le même script sans le header machin affiche bien en
données pures ce qu'il faut, ce n'est pas là que se trouve le problème.

Attention au _GET[fic] qui écrit dans un log: si je peux appeler en http
le fichier de log en question alors je peux exécuter du code sur ton
serveur en remplissant $_GET['fic'] par du code php.

Enfin, si on utilise un buffer, on peut laisser php le flusher ou le
forcer, par habitude j'utilise:
ob_flush();
exit();
pour tout forcer explicitement, mais ob_end_flush() ou rien du tout
devraient fonctionner.

Un autre exemple dans le manuel:
http://fr2.php.net/manual/en/function.readfile.php


HTH
JGA
Vincent Verdon
2010-08-25 04:26:42 UTC
Permalink
Bonsoir,
Post by Vincent Verdon
Bonjour,
Post by Vincent Verdon
amont depuis un CMS (en l'occurence SPIP). Le pb est que certaines
personnes connectées ne perviennet pas à télécharger l'intégralité des
fichiers qu'elles souhaitent : les fichier se trouvent ainsi corrompus.
Chez moi, spip et le safe mode font mauvais ménage. Bien vérifier qu'il
n'est pas activé.
Oui, mais dans ce cas, je devrais moi-même rencontrer ces problèmes lors
d'essais de téléchargements.
Post by Vincent Verdon
Post by Vincent Verdon
--------------------
// on récupère les infos sur le fichier et on crée l'entête
Oui mais non: si les headers sont déjà partis on est morts. Donc un
1) commencer en PREMIERE LIGNE et SANS caractère quel qu'il soit avant
<?php
ob_start();
J'ai expérimenté ça : j'ai déjà cherché un bon moment où était l'erreur
alors qu'il s'agissait d'un caractère non visible en tête du fichier
(avant le premier <?php) : un coup du changement d'encodage d'une
machine à l'autre !
Post by Vincent Verdon
Ensuite avant de construire le contenu du résultat, on vire tout ce qui
ob_clean();
Post by Vincent Verdon
$taille = filesize($fic);
$type_mime = def_mime_type($fic);
$f_nom = basename($fic);
//on ecrit l'entete
Perso je le fais après traitement des éventuelles erreurs mais peu
importe, ça doit fonctionner.
Post by Vincent Verdon
header('Content-Type: '.$type_mime);
header('Content-Length: '.$taille);
header('Content-Disposition: attachment; filename='.$f_nom);
header('Content-Transfer-Encoding: binary');
Perso je n'ai jamais utilisé que deux headers: le content type par
exemple ci-dessous pour un csv, et le disposition à l'identique.
header("Content-Type: text/csv; charset=iso-8859-1;");
header("Content-disposition: attachment; filename=$file_name");
Post by Vincent Verdon
} else {
ecrire_log("echec de l'acces au fichier $_GET[fic]");
}
Attention au _GET[fic] qui écrit dans un log: si je peux appeler en http
le fichier de log en question alors je peux exécuter du code sur ton
serveur en remplissant $_GET['fic'] par du code php.
En fait au paravant je fais des tests pour être certain que les données
sont bien le nom d'un fichier valide et pas autre chose.
Post by Vincent Verdon
Enfin, si on utilise un buffer, on peut laisser php le flusher ou le
ob_flush();
exit();
pour tout forcer explicitement, mais ob_end_flush() ou rien du tout
devraient fonctionner.
Je teste un ob_flush et on verra ce que cela donne.
--
Merci de ton aide, amicalement, Vincent Verdon
Pierre Maurette
2010-08-18 09:17:29 UTC
Permalink
Post by Vincent Verdon
Bonjour,
plutôt débutant en php (ma langue naturelle est le TCL ;-) ), je bute sur un
j'ai créé un ensemble (modeste) de scripts dont le but est de gérer l'accès à
des répertoires sur un serveur web. Il s'agit en fait d'une zone de
téléchargement qui n'est pas en accès libre mais nécessite d'être authentifié
au préalable. Cette authentification est faite en amont depuis un CMS (en
l'occurence SPIP). Le pb est que certaines personnes connectées ne perviennet
pas à télécharger l'intégralité des fichiers qu'elles souhaitent : les
fichier se trouvent ainsi corrompus. Je ne comprends pas pourquoi, et je n'ai
jamais eu personnellement ce problème quand je fais des essais ! Je suppose
1) C'est lié à l'OS de l'utilisateur (perso, j'utilise Linux) ;
2) C'est lié au fournisseur d'accès : le script est sur un site hébergé chez
Free et je suis moi-même chez Free.
3) Je programme bien mal, ce qui est fort possible.
Le 2 n'est pas impossible. Il y a plusieurs années, chez Free
également, il m'était impossible d'uploader sans erreur - fichiers
manquants - en une fois dès que ça dépassait quelques fichiers et / ou
une arborerscence simplissime, en utilisant tout bêtement les bons
clients FTP du moment. Mais j'étais en RTC.
J'avais résolu le problème en zippant l'arborescence et en uploadant
cette archive. Je n'avais ensuite plus qu'à décompresser sur le site, à
l'aide d'un script PHP. J'utilisais soit pclzip, soit des fonctions de
PEAR, il semble que j'avais choisi le premier. J'avais également
préparé l'inverse, c'est à dire compresser une arborescence sur le
serveur, je ne sais plus pouirquoi, peut-être simplement pour ne pas
mourir idiot.

Aujourd'hui, je n'ai plus trop de problème. ADSL, moins d'uploads
massifs - je n'ai plus de sites de photos -, utilisation de Dreamweaver
puis aujourd'hui Eclipse - Aptana. Ceci dit il m'arrive dans Eclipse de
devoir télécharger à nouveau quelques fichiers. En fait, il y a en FTP
(FTPS, etc.) des régressions dans Aptana Studio depuis queqlues mois.
--
Pierre Maurette
Web Dreamer
2010-08-27 11:46:51 UTC
Permalink
Vincent Verdon a écrit ce dimanche 15 août 2010 11:31 dans
Post by Vincent Verdon
Bonjour,
plutôt débutant en php (ma langue naturelle est le TCL ;-) ), je bute
j'ai créé un ensemble (modeste) de scripts dont le but est de gérer
l'accès à des répertoires sur un serveur web. Il s'agit en fait d'une
zone de téléchargement qui n'est pas en accès libre mais nécessite
d'être authentifié au préalable. Cette authentification est faite en
amont depuis un CMS (en l'occurence SPIP). Le pb est que certaines
personnes connectées ne perviennet pas à télécharger l'intégralité des
fichiers qu'elles souhaitent : les fichier se trouvent ainsi corrompus.
Je ne comprends pas pourquoi, et je n'ai jamais eu personnellement ce
1) C'est lié à l'OS de l'utilisateur (perso, j'utilise Linux) ;
Je donne des accès au téléchargement à de "gros" fichiers à des clients, et
j'ai déjà rencontré ceci qui est lié à leur OS:

IE6 ne télécharge pas plus de 2Go (4Go pour IE7).

Donc par ex. une image iso de distrib linux en téléchargement (pouvant faire
4,7Go) se verra tronquée (donc corrompue) à 2 Go (ou 4).
Seul IE8 n'a plus cette limite.
De plus, il existe des utilisateurs Windows qui on upgradé au file du
temps... et gardés un disque avec une partition FAT32... qui ne supporte pas
plus de 4Go "par fichier". Et parfois ils utilisent ce disque comme cible
pour le téléchargement...
J'en ai eu d'autres essayant de sauvegarder une image iso téléchargée sur un
clé USB de 8 Go pensant que ça irait, mais les clés USB de 8Go sont souvent
en FAT32... et un fichier unique ne peut dépasser 4Go.

Donc je ne sais pas la taille des fichiers que tu laisses en téléchargement,
mais si ils sont gros, tu as cette limitation décrite ci-dessus pour les
utilisateurs de Windows.

Donc ce que tu peux faire coté PHP, c'est de détecter le butineur du client
(avec $_SERVER['HTTP_USER_AGENT'] ou get_browser() ) et en fonction,
"griser" (et inactiver) les liens affichés vers les fichiers de taille non
supportés et ajouter un lien proposant le téléchargement d'un autre
navigateur avec un message explicite expliquant l'impossibilité de leur
navigateur actuel à télécharger un tel fichier.:

if (filesize('fichier.iso') > $browserlimit)
{
...ton code qui affiche le lien spécial...
}
else
{
... ton code qui affiche le lien normalement...
}

où $browserlimit a été initialisé à une certaine valeur en fonction de
$_SERVER['HTTP_USER_AGENT']
De tête je ne sais plus ce que donne IE, j'en ai pas sous la main :-) donc
je peux pas te donner le code "de mémo", mais un bête if() ou switch() (et
case ) suffit et pour les autres butineurs tu initialise $browserlimit à une
GROSSE valeur)

Par contre... tu ne peut pas détecter la partition où le client va
sauvegarder son fichier... (du moins pas avec PHP, sauf a détecter dans le
$_SERVER['HTTP_USER_AGENT'] une version de win9x qui ne serra QUE en FAT32,
mais ceux qui utilisent encore un tel OS non-mis à jour sont suicidaires)
mais détecter le browser est déjà une épine du pied de retiré.
--
Web Dreamer
Vincent Verdon
2010-09-02 22:52:02 UTC
Permalink
Bonsoir,
Post by Web Dreamer
Je donne des accès au téléchargement à de "gros" fichiers à des clients, et
IE6 ne télécharge pas plus de 2Go (4Go pour IE7).
Le problème semble bien être celui que j'avais imaginé : Free limite le
téléchargement en php s'il n'est pas notre fournisseur d'accès. J'ai
fais l'expérience depuis chez moi (aucun problème) puis depuis un autre
accès non Free et là je suis presqu'inmanquablement coupé en milieu de
téléchargement, en général au bout de 2Mo mais parfois plus. Les
fichiers à télécharger font environ 5 Mo (c'est pourtant pas beaucoup
!). Je suis très loin des 2Go...
--
Amicalement, Vincent Verdon
Christophe Bachmann
2010-09-03 09:59:27 UTC
Permalink
Post by Vincent Verdon
Bonsoir,
Le problème semble bien être celui que j'avais imaginé : Free limite le
téléchargement en php s'il n'est pas notre fournisseur d'accès.
Est-ce spécifique à PHP ou est-ce un délai limite. Je sais que la
vitesse de transfert du service de téléchargement Free est très
inférieure quand on n'est pas sur leurs lignes, ce qui pourrait causer
un time-out.
--
Greetings, Salutations,
Guiraud Belissen, Château du Ciel, Drachenwald,
Chris CII, Rennes, France
Vincent Verdon
2010-09-08 21:54:33 UTC
Permalink
Bonsoir,
Post by Christophe Bachmann
Post by Vincent Verdon
Bonsoir,
Le problème semble bien être celui que j'avais imaginé : Free limite le
téléchargement en php s'il n'est pas notre fournisseur d'accès.
Est-ce spécifique à PHP ou est-ce un délai limite. Je sais que la
vitesse de transfert du service de téléchargement Free est très
inférieure quand on n'est pas sur leurs lignes, ce qui pourrait causer
un time-out.
Pas au bout de 10 ou 20 s quand même.
De toute façon, que puis-je y faire ?
--
Amicalement, Vincent Verdon
Continuer la lecture sur narkive:
Loading...