Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
Bonjour,
J'ai besoin d'autres yeux sur mon code parce que je ne vois pas le problème
J'essaie d'envoyer au client un fichier exe dans une action de controller.
Le fichier envoyé fait bien la bonne taille, je peux l'enregistrer, mais il ne fonctionne pas, c'est un exe, donc fenêtre noire et rien du tout.
Si je télécharge le fichier directement depuis le ftp, il fonctionne très bien.
Mon action :
function downloadAction() { $this->_helper->viewRenderer->setNoRender(); if (isset($this->getRequest()->fn)) { if (eregi($this->getRequest()->fn,$this->user->servicesautorises)) { $f_name = $this->getRequest()->fn . '-Installer.exe'; $f_location = Zend_Registry::get('config')->download->src_download_root . '/' . $f_name; header('Content-Disposition: attachment; filename='.$f_name.';'); header('Content-Type: application/octet-stream'); header('Content-Transfer-Encoding: binary\n'); header('Content-Length: ' . filesize($f_location)); header('Pragma: no-cache'); header('Cache-Control: must-revalidate, post-check=0, pre-check=0, public'); header('Expires: 0'); readfile($f_location); } else { $this->_redirect('/'); } } }
Une idée ? ou une meilleure méthode peut-être ?
Merci.
A+ benjamin.
Dernière modification par Delprog (14-10-2008 00:40:23)
Hors ligne
salut Benjamin,
quelques questions pour mieux comprendre ce qui se passe :
n'aurais-tu pas d'envoi de headers quelconques avant de dumper ton fichier ?
Es-tu sûr que le fichier fait exactement la même taille au départ et à l'arrivée ?
As-tu fait un md5 avant et après l'envoi pour contrôler l'intégrité de ton fichier durant le transfert ?
Enfin, peux-tu m'éclairer sur cette "fenêtre noire" ? Je ne comprends pas bien ; si ton fichier est corrompu, une erreur devrait être générée lors de l'exécution...
Hors ligne
Salut
Non je n'ai aucun envoi de header avant puisque le seul traitement que je maitrise avant l'appel de l'action c'est mon bootstrap, qui ne fait donc aucun envoi de "header" (encore heureux :p)
J'ai un plugin pour la route, mais idem, il n'envoie surtout pas de header.
Sinon le fichier téléchargé et le fichier d'origine font exactement la même taille d'après les propriétés de windows, mais ne sont pas les mêmes. Si je regarde dans les propriétés de chacun, j'ai une différence dans l'onglet "Archive".
Fichier d'origine :
SFX ZIP archive (Mais c'est bien un exe hein)
Version à extraire 2.0
Système d'exploitation hôte Windows
Nombre total de fichiers 21
Longueur totale 11 303 589
Longueur compressée 4 708 810
Taux 41%
Taille du module SFX 34 324
Commentaire principal Absent
Mots de passe Absent
Fichier téléchargé :
SFX ZIP archive (Mais c'est bien un exe hein)
Version à extraire 2.0
Système d'exploitation hôte DOS
Nombre total de fichiers 20
Longueur totale 11 303 433
Longueur compressée 4 708 676
Taux 41%
Taille du module SFX 34 107
Commentaire principal Absent
Mots de passe Absent
Donc visiblement il n'arrive pas entier Mais je ne sais pas d'où ça peut venir...
Pour la "Fenêtre noire" j'ai eu un élan de neuneu pour pondre cette expression. Il s'agit biensûr d'une fenêtre DOS. Si j'exécute l' exe en command pour voir le message j'ai :
Program too big to fit in memory
Voilà
A+ benjamin.
Dernière modification par Delprog (13-10-2008 15:21:14)
Hors ligne
essaie au lieu de readfile :
$this->_response->clearBody(); //efface le body de la reponse (aucas ou ..) $chaine = file_get_contents(<monFichier>); // recupere le contenu du fichier $taille = strlen($chaine); // recupere la taille $this->_response->setBody($chaine); //puis dans les headers : $this->_response->setHeader('Content-Length',$taille,true); //header avec la taille du fichier
c'est juste une idee comme ca sans garantie
je m'en sers qu'en j'envoie du pdf
Hors ligne
Même problème
Donc mon nouveau code :
function downloadAction() { $this->_helper->viewRenderer->setNoRender(); if ((isset($this->getRequest()->fn))&&(eregi($this->getRequest()->fn,$this->user->servicesautorises))) { $f_name = $this->getRequest()->fn . '-installer.exe'; $f_location = Zend_Registry::get('config')->download->src_download_root . '/' . $f_name; $f_string = file_get_contents($f_location); // recupere le contenu du fichier $f_size = strlen($f_string); // recupere la taille $this->_response->clearHeaders(); $this->_response->clearBody(); $this->_response->setBody($f_string); $this->_response->setHeader('Content-Disposition', 'attachment; filename='.$f_name.';', true); $this->_response->setHeader('Content-Type', 'application/octet-stream', true); $this->_response->setHeader('Content-Transfer-Encoding', 'binary', true); $this->_response->setHeader('Content-Length',$f_size,true); $this->_response->setHeader('Pragma','no-cache',true); $this->_response->setHeader('Cache-Control','must-revalidate, post-check=0, pre-check=0, public',true); $this->_response->setHeader('Expires',0, true); } else { $this->_redirect('/private'); } }
C'est plus propre, mais ça ne change rien.
Dernière modification par Delprog (13-10-2008 17:42:51)
Hors ligne
Par contre dans ton code tu fais :
.... $f_string = file_get_contents($f_location); .... $this->_response->setBody($f_string);
Mais setBody est censé recevoir un string et pas du binaire non ? (sauf erreur de ma part)
A+ benjamin.
Hors ligne
Bon, pour prouver que les fichiers sont bons, ça fonctionne en faisant ça :
function downloadAction() { $this->_helper->viewRenderer->setNoRender(); if ((isset($this->getRequest()->fn))&&(eregi($this->getRequest()->fn,$this->user->servicesautorises))) { $f_name = $this->getRequest()->fn . '-installer.exe'; $f_location = Zend_Registry::get('config')->download->src_download_root . '/' . $f_name; $this->_redirect('/' . $f_location); exit; } else { $this->_redirect('/private'); } }
Ce qui ne me convient puisque je veux justement protéger l'accès aux fichiers en fonction des droits de l'utilisateur.
Dernière modification par Delprog (13-10-2008 18:15:07)
Hors ligne
euh si je comprends bien tu fais un simple redirect là ; autrement dit, tu ne contrôles pas vraiment l'accès au fichier, puisque quelqu'un qui connaitrait l'URL pourrait y accéder sans souci...
Je repense à un truc... en relisant tes posts, je m'aperçois que tu sembles être sous Windows, lequel gère les binaires d'une façon spécifique.
essaies de faire ça (dans ton premier code) :
// readfile($f_location); $fp = fopen($f_location,"rb"); fpassthru($fp);
Ca peut aider - la différence de taille entre l'original et la copie pourrait bien correspondre à certains caractères binaires manquants...
Hors ligne
Pardon, il manquait un "pas" dans ma phrase il fallait lire :
Ce qui ne me convient pas
Le redirect c'était pour prouver que ça ne venait pas du fichier, ce ne sera en aucun cas ma solution :p
Effectivement je suis sous windows (*remontée de bile acide*)
Je teste ça desuite
Dernière modification par Delprog (13-10-2008 23:17:28)
Hors ligne
Ca fonctionne très bien merci.
Par contre pour info, comme 'fpassthru' dirige immédiatement le résultat vers la sortie, si on veut utiliser la syntaxe :
$this->getResponse()->setHeader(....);
à la place de :
header(....);
(pour être vraiment propre jusqu'au bout)
il faut forcer l'envoie de la réponse par un :
$this->getResponse()->sendResponse();
avant d'envoyer 'fpassthru'
Donc mon code final :
function downloadAction() { $this->_helper->viewRenderer->setNoRender(); if ((isset($this->getRequest()->fn))&&(eregi($this->getRequest()->fn,$this->user->servicesautorises))) { // Si l'user est autorisé à dl le fichier et si le paramètre fn est passé $f_name = $this->getRequest()->fn . '-installer.exe'; $f_location = Zend_Registry::get('config')->download->src_download_root . '/' . $f_name; if (file_exists($f_location)) { // Continue seulement si le fichier existe $f_size = filesize($f_location); $fp = fopen($f_location,"rb"); $this->getResponse()->clearHeaders() ->setHeader('Content-Description', 'File Transfer', true) ->setHeader('Content-Disposition', 'attachment; filename='.$f_name.';', true) ->setHeader('Content-Type', 'application/octet-stream', true) ->setHeader('Content-Transfer-Encoding', 'binary', true) ->setHeader('Content-Length',$f_size,true) ->setHeader('Pragma','no-cache',true) ->setHeader('Cache-Control','must-revalidate, post-check=0, pre-check=0, public',true) ->setHeader('Expires',0, true) ->sendResponse(); // force l'envoie immédiat des headers pour bien attraper le fpassthru fpassthru($fp); fclose($fp); exit; } else { // Si le fichier n'existe pas : mauvais paramètre fn throw new Zend_Controller_Action_Exception(Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION,404); exit; } } else { $this->_redirect('/private'); } }
Merci pour votre aide.
A+ benjamin.
Hors ligne
en effet fpassthru() dump directement le contenu du fichier sur la sortie standard...
en tout cas, je suis content d'avoir pu t'aider, même si ce fut laborieux (j'aurais du y penser plus vite, mais comme je n'utilise à peu près jamais PHP sous Windows...)
Hors ligne
mais comme je n'utilise à peu près jamais PHP sous Windows...
Et comme tu es chanceux, si j'avais le pouvoir de décision sur les serveurs, crois moi qu'on serait en Linux. Pas que je sois anti microsoft, mais malgré Zend qui optimise les performances avec le FastCGI, on est encore très loin des performances d'un serveur LAMP.
Hors ligne