Mail + pièce jointe PDF généré en PHP

WRInaute occasionnel
Bonjour,

J'ai un script qui génère des bons de livraison (BL) en PDF ou en HTML (suivant un paramètre html=0 ou 1 dans l'url)
Après avoir épluché quelques posts, j'ai mis en place, en bas du BL HTML une fonctionnalité qui permet de saisir une adresse mail pour envoyer le BL au format PDF en PJ.

Le mail parvient bien au destinataire, avec une PJ au format PDF: mais ce fichier pdf joint est vide (ou réduit à 0 je ne sais pas): il fait 0 ko et ne peut être ouvert pas Adobe Reader.

J'ai tenté plusieurs soluces, mais en vain.

Explications techniques:
genrer_BL.php génère le BL au format DF ou HTML. Le format HTML affiche en plus un formulaire pour saisir l'adresse mail.
envoyer_mailBL.php reçoit le $_POST, traite l'adresse, envoie le mail avec ça comme code:
Code:
	// Destinataire du mail
	$destination = $_POST['mail_dest_bl'];
	// Expediteur du mail
	$from_mail = 'monadresse@mondomaine.com';
	$from_name = "Prénom NOM";
	// sujet du mail
	$sujet = '[Mon entreprise] - votre BL';
	// Chaine permettant de différencier les différentes parties du mail
	$uid = md5(uniqid(time()));
	$message = "Bonjour,\nVous trouverez en pièce jointe le BL demandé au format PDF.\n";
	$message .= $signature_mail;
	
	// Traitement pour attacher une PJ
	// D'abord on lit le fichier
	$fichier = 'generer_BL.php?id_course='.$_POST['id_course'].'&pj=BL-'.$_POST['num_BT'].'.pdf&html=0';
	$attachement = chunk_split(base64_encode($fichier));
	
	$header = "From: ".$from_name." <".$from_mail.">\r\n";
	$header .= "Reply-To: ".$from_mail."\r\n";
	$header .= "MIME-Version: 1.0\r\n";
	$header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\r\n\r\n";
	$header .= "This is a multi-part message in MIME format.\r\n";
	$header .= "--".$uid."\r\n";
	$header .= "Content-type:text/plain; charset=iso-8859-1\r\n";
	$header .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
	$header .= $message."\r\n\r\n";
	$header .= "--".$uid."\r\n";
	$header .= "Content-Type: application/pdf; name=\"BL-".$_POST['num_BT'].".pdf\"\r\n";
	$header .= "Content-Transfer-Encoding: base64\r\n";
	$header .= "Content-Disposition: attachment; filename=\"BL-".$_POST['num_BT'].".pdf\"\r\n\r\n";
	$header .= $attachement."\r\n\r\n";
	$header .= "--".$uid."--";	
	// enfin on envoi le mail
	$is_sent = @mail($destination, $sujet, "", $header);
	if ($is_sent)
	{
		header('Location: generer_BL.php?id_course='.$_POST['id_course'].'&html=1&mail=ok');
	}
	else echo 'Envoi raté:<br />'.$headers;
Avec $_POST['num_BT'] et $_POST['id_course'] qui étaient en type="hidden" dans le formulaire.

generer_BVL.php génère le PDF avec mPDF (super d'ailleurs). Lorsqu'il est appelé avec le paramètre pj, il ressort le PDF avec ça:
Code:
	if (isset($_GET['pj']))
		$livraison_mpdf -> Output('', 'S');
	else
		$livraison_mpdf -> Output();
	exit;

Où peut se trouver mon souci ?
 
WRInaute accro
Au lieu de réinventer toute la roue, tu as essayé l'envoi avec des libs style PHPMailer ou Swift Mailer ?
 
WRInaute discret
d'accord avec spout, swiftmailer va te permettre de faire ca simplement, et c'est une mecanique eproouvee...
 
WRInaute occasionnel
Wouaiiiis, bon d'accord. Cela me semblait être comme "utiliser une bombe nulcéaire pour tuer une mouche".
Je ne pense d'ailleurs pas que le souci soit logiciel/applicatif.

Mais bon, vous n'êtes pas les premiers à me le dire. Je tente PHPMailer et je reviens...
 
WRInaute accro
Il suffit de pas grand chose, lors de la constitution des en-têtes d'un mail, pour merdoyer et envoyer un truc mal interprété ou planté.

Tu as vérifié que le fichier que tu essaies d'envoyer était correctement généré ?
 
WRInaute occasionnel
Tu veux dire si ses en-têtes http l'étaient ?

En début de script PHP générateur du PDF, j'ai ça (donné par la web dev toolbar de Google Chrome):
Code:
include ($chem."mpdf51/mpdf.php");
qui se charge d'envoyer les en-têtes correct (j'espère).

Lorsque je génère le pdf "à la main", j'obtiens ça:
Code:
Request URL:http://plateforme.transporturgent.com/courses/generer_BL.php?id_course=7940&html=0
Request Method:GET
Status Code:200 OK
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
Cache-Control:max-age=0
Connection:keep-alive
[...]
Host:plateforme.transporturgent.com
If-Modified-Since:Sat, 30 Jul 2011 13:39:00 GMT
User-Agent:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.122 Safari/534.30
Query String Parametersview URL encoded
id_course:7940
html:0
Response Headers
Cache-Control:public, must-revalidate, max-age=0
Connection:Keep-Alive
Content-Encoding:gzip
Content-Length:44543
Content-Type:application/pdf
Content-disposition:inline; filename="mpdf.pdf"
Date:Sat, 30 Jul 2011 13:39:49 GMT
Expires:Sat, 26 Jul 1997 05:00:00 GMT
Keep-Alive:timeout=15, max=100
Last-Modified:Sat, 30 Jul 2011 13:39:51 GMT
Pragma:public
Server:Apache/2.2.16 (Unix) mod_ssl/2.2.16 OpenSSL/0.9.8o
Vary:Accept-Encoding,User-Agent
X-Powered-By:PHP/5.2.13-pl1-gentoo

(J'ai supprimé des infos concernant les cookies)

C'est bien ou pas ?
 
WRInaute accro
A moins que j'aie besoin de lunettes, il manque quand même un énorme truc dans ton script: générer ou récupérer le PDF!

Tu fais:
Code:
$fichier = 'generer_BL.php?id_course='.$_POST['id_course'].'&pj=BL-'.$_POST['num_BT'].'.pdf&html=0';
$attachement = chunk_split(base64_encode($fichier));

Bref, ton fichier attaché, c'est juste l'URL (relative), pas le fichier lui-même. Si tu veux utiliser la logique que tu sembles privilégier (i.e. appeler le générateur de PDF à travers un appel http), il te manque un file_get_contents ou équivalent. Mais ça pose probablement un problème d'authentification (je suppose que tout le monde n'a pas accès à ce script), et c'est probablement un peu lourd pour pas grand chose, tu dois pouvoir gérer le PDF en interne directement, non?

Au passage, l'utilisation de $_POST['truc'] sans filtrage, sans escape, sans rien, c'est quand même très dangereux.

Et l'utilisation de librairies adaptées pour générer le mail ce n'est effectivement pas forcément une mauvaise idée.

Jacques.
 
WRInaute accro
+1 Jcaron. En fait ce que je te demandais c'est si le fichier créé physiquement était correcte ou pas. Or a priori, tu ne génères pas ce fichier, je vois mal dans ce cas comment tu peux l'envoyer en PJ.
 
WRInaute occasionnel
Au passage, l'utilisation de $_POST['truc'] sans filtrage, sans escape, sans rien, c'est quand même très dangereux.
ici, ce n'est pas dangereux.

Ce que vous dites est juste. J'ai bien tenté de faire un file_get_content ou un fgets (je ne me souviens plus), mais j'avais alors un problème de récursivité.
J'ai bientôt terminé mon test avec phpmailer. Je vous tiens au jus.
 
WRInaute accro
Il est très, très rare que ce ne soit pas dangereux. Les gens mal intentionnés ont généralement plus d'imagination que les ceux qui développent.

Jacques.
 
WRInaute occasionnel
PhpMailer me répond:
Code:
Language string failed to load: recipients_failedmediaprodx@gmail.com

Kesako ? Comment réparer ?
 
WRInaute occasionnel
Merci.
Bon, tout est OK: mes mails partent (et arrivent), cela fonctionne aussi avec une PJ 'en dur' (un fichier pdf stocké à côté du script), mais lorsque je lui demande d'attacher mon fichier pdf généré à la volée comme ça:
Code:
	$fichier = 'generer_BL.php?id_course='.$_POST['id_course'].'&pj=BL-'.$_POST['num_BT'].'.pdf&html=0';
	$mail->AddAttachment($fichier);

on me répond
Code:
Could not access file: generer_BL.php?id_course=3350&pj=BL-003020811.pdf&html=0

C'est une question d'identification ?
Peut-être faut-il générer le fichier, puis l'enregistrer, l'attacher au mail, et accessoirement le supprimer ensuite ?
 
WRInaute accro
xdeslandes a dit:
Peut-être faut-il générer le fichier, puis l'enregistrer, l'attacher au mail, et accessoirement le supprimer ensuite ?

Je pense plutôt que c'est ça. perso je n'ai jamais procédé autrement.
 
WRInaute accro
Je pense aussi, car là à part une url $fichier contient rien, faut te servir de curl pour générer le fichier non ?
 
WRInaute occasionnel
Merc UsagiYojimbo.
Du coup, dans mon script, je dois "déclencher" la génération du fichier, puis attacher ce fichier.
Mais comment puis-je enregistrer le fichier généré (cela devient un autre problème !): avec un file_get_contents ??
 
WRInaute occasionnel
Oui, spout, j'avais bien pris connaissance de cette doc, mais comment faire appel à ce fichier ? Comment juste déclencher la génération du doc php tout en poursuivant le script d'envoi du mail ?
Dans l'idéal:
1- je génère le doc pdf avec output('nomfichier.pdf', 'F')
2- je joint le document généré avec PHPMailer
3- je supprime le document

Si j'appelle directement le script generer_BL.php, pas de soucis: le fichier nomfichier.pdf est bien présent sur le serveur, et je
le joins sans souci dans mon mail.
Mon problème est de déclencher la phase 1...
 
WRInaute occasionnel
Je viens de tenter ça:
Code:
	$nom_fichierBL = 'BL-123456789.pdf';
	$fichier = 'generer_BL.php?id_course='.$_POST['id_course'].'&pj='.$nom_fichierBL.'&html=0';
	file_put_contents($nom_fichierBL, $fichier);
	
	// envoi du mail avec PhpMailer
	$mail = new PHPmailer();
	$mail -> SetLanguage('en', $chem.'phpmailer/language/');
//	$mail->IsSMTP();
//	$mail->Host = 'smtp.transport-urgent.com';
	$mail->From = 'exploitation@transporturgent.com';
	$mail->AddAddress($destination);
	$mail->AddReplyTo($from_mail, $from_name);	
	$mail->Subject = $sujet;
	$mail->Body = $message;
	$mail->AddAttachment($nom_fichierBL);

Et ça marche ...presque: le fichier est bien généré, il est ensuite joint à au mail. Mais ce fichier pdf ne fait que 56 octets (au lieu d'environ 50ko) et n'est pas lisible. Ce doit être un pb au niveau de mon file_get_contents...
 
WRInaute accro
file_put_contents met dans un fichier des données que tu as déjà en mémoire, pas le contenu d'un autre fichier. Il faut donc d'abord que tu récupères le fichier avec file_get_contents, ou que tu utilises copy() pour faire directement la copie.

Mais:
- il faut que tu donnes une URL complète, pas juste une URL relative comme tu la construits jusque là
- il est très vraisemblable que ça ne marchera pas parce que tu ne seras pas authentifié (je suppose que tout le monde n'a pas accès à ces URLs sans login ni rien...).

Ce serait donc probablement plus efficace que tu appelles le code qui génère le PDF en lui disant de stocker tout ça dans un fichier. Si le code correspondant est déjà dans une fonction, il suffit de l'appeler. Dans le cas contraire, il faut que tu découpes ton script generer_BL.php en deux parties:
- une partie qui contient une fonction avec le code de génération lui-même
- une partie qui ne fait que un include du fichier précédent + appel de la fonction

Ensuite dans le script d'envoi, un include du premier fichier + appel de la fonction, et hop.

Jacques.
 
WRInaute occasionnel
Merci pour ces tuyaux Jacques. Mais
jcaron a dit:
Ce serait donc probablement plus efficace que tu appelles le code qui génère le PDF en lui disant de stocker tout ça dans un fichier.
C'est pourtant ce que je fais (voir mon code plus haut), non ? Je ne comprends pas...
 
WRInaute accro
Non, tu donnes juste un bout d'URL. Ce qu'il faut que tu fasses, c'est intégrer le code correspondant (via un include).

Par exemple, si ton generer_BL.php c'est:
Code:
<?php
toto();
titi();
tutu(param1,param2);
?>

Il faut que tu découpes ça en un fichier qui contient le boulot, par exemple generer_BL.inc.php, qui contient:
Code:
function generer_BL($param1,$param2)
{
  toto();
  titi();
  tutu($param1,$param2);
}

et ton generer_BL.php devient:
Code:
<?php
include('generer_BL.inc.php');
generer_BL(param1,param2);
?>

Comme ça, tu peux inclure generer_BL.inc.php aussi dans ton script d'envoi de mail, et appeler la fonction au bon endroit pour generer le BL (en changeant les paramètres pour qu'il stocke ça dans un fichier plutôt que d'afficher.

Jacques.
 
WRInaute occasionnel
Yessss ! Merci Jacques.
Cela m'a fait brasser pas mal mon code, mais cela fonctionne bien.

Il me reste un détail propre à PHPMailer: le mail provient de Root User, alors que je lui ai indiqué
Code:
$mail->AddReplyTo($from_mail, $from_name);
avec $from_name='mon nom';

Et en mettant
Code:
$mail->SetFrom($from_mail, $from_name);
on a une erreur:
Code:
Fatal error: Call to undefined method PHPMailer::SetFrom() in /home/transpor/www/courses/envoi_mailBL.php on line 65

Je ne trouve rien dans la doc officielle (qui mentionne pourtantcette méthode)
 
WRInaute occasionnel
Moi je vois un autre problème à ta manière de proceder et cette fois ci c'est pas dans le code.
Les règles emailing utilisées par les FAI et webmails font qu'ils n'aiment pas trop les pièces jointes, trop souvent associées à la propagations de virus ou de malwares.
Pourquoi ne pas générer ton fichier sur ton serveur, et mettre dans ton mail un lien vers celui-ci ?
Ainsi plus de fichier joint et moins de risques pour ta délivrabilité.

Oui, on est loin du codage pur là :)
 
WRInaute occasionnel
J'y ai bien pensé, mais à 80-100ko le pdf et à raisons de 5 à 10 envois par jour, cela fait du stokage inutile. Il faudrait alors penser à un système de conservation pendant N jours ou un truc du genre. J'ai aussi écarté cette solution car ces pdf peuvent être générés à la volée et sont donc dispo en permanence, sans qu'ils soient stockés quelquepart.
Merci en tous cas.
 
Discussions similaires
Haut