Mail

PHP : Envoi d’un mail avec pièce jointe

Le jour où j’ai du créer un formulaire permettant d’envoyer un mail avec pièce jointe, j’ai mis un certain temps avant de résoudre ce problème. En effet, malgré de nombreux tutos trouvés sur Internet, qu’ils soient français ou anglais, je n’ai jamais réussi à faire marcher complètement leur code, notamment pour Gmail. Pour finir, ce n’est qu’après avoir afficher le code source d’un mail pour essayer de le décrypter que j’ai réussi à envoyer le fameux mail.
Alors pour vous éviter la même galère, voici un petit tuto.

Créer le formulaire

Tout d’abord, il nous faut un formulaire pour appeler le fichier qui s’occupera d’envoyer le mail.
Dans ce formulaire, nous autorisons l’utilisateur à envoyer une image en JPEG ou un PDF avec une taille maximale de 2Mo. On lui demande également son nom, son mail et un message. La partie action=’mail.php’ permet d’indiquer que le formulaire envoie ses données au fichier mail.php quand l’utilisateur appuie sur le bouton Envoyer.

<form action="mail.php" method="post" enctype="multipart/mixed">
	<label for="nom">Votre nom</label>
	<input type="text" name="nom" required/>
	<label for="email">Votre E-mail</label>
	<input type="email" name="email" required/>
	<label for="message">Votre demande</label>
	<textarea name="message" rows="2" cols="50" required></textarea>
	<label for='fichier'>Ajouter une pièce jointe <span>(jpeg ou pdf, max 2Mo)</span></label>
        <input type="file" name="fichier" id="fichier">
	<input type="submit" value="Envoyer">
</form>

Récupération des données

Créons maintenant le fichier mail.php. Vu que notre formulaire utilise la méthode POST pour envoyer les données, on les récupère avec $_POST[‘nom_de_la_variable’]. Si nous avions utilisé la méthode GET on aurait utilisé $_GET[‘nom_de_la_variable’]. Le nom de la variable est ce qui a été saisi dans l’attribut « name » de chaque input, ici nom, email et message. Pour éviter des problèmes comme une insertion de script on utilise la fonction htmlentities() pour sécuriser nos données.

Pour signifier un passage à la ligne on peut utiliser soit \r\n soit \n. Cependant tous les fournisseurs ne prennent pas en charge le \n seul. Pour cela on vérifie si ce dernier le supporte, en vérifiant si l’adresse entrée est chez hotmail, live ou msn (qui le supportent).

Finalement on configure l’adresse mail à laquelle doit être envoyer le mail (ici moi-même), on écrit le sujet du mail que l’on veut recevoir (il est également possible de le récupérer via le formulaire si vous avez un champ « Sujet » par exemple), et on crée une clé aléatoire de limite ou boundary. Cette boundary permet de séparer le corps du message de la pièce jointe et ainsi permettre son envoi.

<?php

error_reporting(E_ALL); 
ini_set("display_errors", 1); //Affichage des erreurs

//Eviter les insertions de scripts dans le cas d'un e-mail HTML
$nom = htmlentities($_POST['nom']);
$email_from = htmlentities($_POST['email']); 
$message = htmlentities($_POST['message']);

//Verifie si le fournisseur prend en charge les r
if(preg_match("#@(hotmail|live|msn).[a-z]{2,4}$#", $email_from)){
    $passage_ligne = "\n";
}else{
    $passage_ligne = "\r\n";
}

$email_to = "mon_mail@gmail.com"; //Destinataire
$email_subject = "Contact pour Marine"; //Sujet du mail
$boundary = md5(rand()); // clé aléatoire de limite

Nettoyer le message

Pour éviter que l’utilisateur puisse entrer des données qui provoquerait un mal fonctionnement du mail, on crée une fonction qu’on utilisera pour nettoyer le contenu du message.

<?php

function clean_string($string) {
    $bad = array("content-type","bcc:","to:","cc:","href");
    return str_replace($bad,"",$string);
}

Création du mail

Voilà la partie importante ! La création du mail. Commençons par le header qui permet de dire de la part de qui est le message et le type de contenu. On insère les données dans les différents champs. From donne des informations sur l’émetteur du message (l’utilisateur ici), Reply-to indique à qui on envoie un mail lorsqu’on cliquera sur le bouton répondre, MIME-Version permet d’indiquer que le contenu du message est formaté en MIME et Content-Type permet d’indiquer quel type de contenu est envoyé (par exemple du texte simple ou du html).
Les .= signifient $headers = $headers.’chaine_de_caractères’. Cela veut dire qu’on ajoute du contenu en plus de celui déjà présent dans la variable.

<?php

$headers = "From: ".$nom." <".$email_from.">" . $passage_ligne; //Emetteur
$headers.= "Reply-to: ".$nom." <".$email_from.">" . $passage_ligne; //Emetteur
$headers.= "MIME-Version: 1.0" . $passage_ligne; //Version de MIME
$headers.= 'Content-Type: multipart/mixed; boundary='.$boundary .' '. $passage_ligne; 
//Contenu du message (alternative pour deux versions ex:text/plain et text/html

Maintenant que l’on a configuré le début de mail, il nous faut afficher le contenu du message. On ouvre tout d’abord la boundary en n’oubliant pas les ‘–‘ avant. On précise ensuite que le message est du texte simple codé en UTF-8 (cela évite des caractères qui s’affichent mal), puis on défini l’encodage de transfert. Finalement on entre le contenu de message nettoyé.

<?php

$email_message = '--' . $boundary . $passage_ligne; //Séparateur d'ouverture
$email_message .= "Content-Type: text/plain; charset=utf-8" . $passage_ligne; //Type du contenu
$email_message .= "Content-Transfer-Encoding: 8bit" . $passage_ligne; //Encodage
$email_message .= $passage_ligne .clean_string($message). $passage_ligne; //Contenu du message

Ajout de la pièce jointe

Le moment tant attendu ! L’ajout de la pièce jointe. On stocke tout d’abord le format du fichier envoyé, la taille du fichier et son nom. On vérifie que ce n’est pas un .htaccess, ce qui pourrait causer de gros problèmes. Si ce n’est pas le cas on vérifie que le format du fichier est bien un JPEG ou un PDF. Si c’est le cas on vérifie que le fichier fait moins de 2Mo. On remplace les caractères spéciaux par des caractères latin, puis on prépare le fichier pour l’envoi. Finalement on ouvre à nouveau la boundary pour signifier un nouveau contenu (une pièce jointe), on écrit les différentes informations concernant le contenu et on ajoute la pièce jointe.

Si une des vérifications n’est pas validée on envoie à la place de la pièce jointe un message d’erreur dans le mail expliquant le problème pour nous permettre de recontacter l’utilisateur si besoin. Ce n’est pas nécessaire si vous avez au préalable réalisé une vérification des données du formulaire en Javascript par exemple. Puis on ferme la boundary pour signifier la fin du mail.

<?php

//Pièce jointe
    if(isset($_FILES["fichier"]) &&  $_FILES['fichier']['name'] != ""){ //Vérifie sur formulaire envoyé et que le fichier existe
        $nom_fichier = $_FILES['fichier']['name'];
        $source = $_FILES['fichier']['tmp_name'];
        $type_fichier = $_FILES['fichier']['type'];
        $taille_fichier = $_FILES['fichier']['size'];
                    
        if($nom_fichier != ".htaccess"){ //Vérifie que ce n'est pas un .htaccess
			 if($type_fichier == "image/jpeg" 
                || $type_fichier == "image/pjpeg" 
                || $type_fichier == "application/pdf"){ //Soit un jpeg soit un pdf
                 
                if ($taille_fichier <= 2097152) { //Taille supérieure à Mo (en octets)
                    $tabRemplacement = array("é"=>"e", "è"=>"e", "à"=>"a"); //Remplacement des caractères spéciaux
                    
                    $handle = fopen($source, 'r'); //Ouverture du fichier
                    $content = fread($handle, $taille_fichier); //Lecture du fichier
                    $encoded_content = chunk_split(base64_encode($content)); //Encodage
                    $f = fclose($handle); //Fermeture du fichier
                                
                    $email_message .= $passage_ligne . "--" . $boundary . $passage_ligne; //Deuxième séparateur d'ouverture
                    $email_message .= 'Content-type:'.$type_fichier.';name="'.$nom_fichier.'"'. $passage_ligne; //Type de contenu (application/pdf ou image/jpeg)
                    $email_message .='Content-Disposition: attachment; filename="'.$nom_fichier.'"'. $passage_ligne; //Précision de pièce jointe
                    $email_message .= 'Content-transfer-encoding:base64'. $passage_ligne; //Encodage
                    $email_message .= $passage_ligne; //Ligne blanche. IMPORTANT !
                    $email_message .= $encoded_content. $passage_ligne; //Pièce jointe

                }else{
					//Message d'erreur
                    $email_message .= $passage_ligne ."L'utilisateur a tenté de vous envoyer une pièce jointe mais celle ci était superieure à 2Mo.". $passage_ligne;
                }
            }else{
				//Message d'erreur
                $email_message .= $passage_ligne ."L'utilisateur a tenté de vous envoyer une pièce jointe mais elle n'était pas au bon format.". $passage_ligne;
            }
        }else{
			//Message d'erreur
            $email_message .= $passage_ligne ."L'utilisateur a tenté de vous envoyer une pièce jointe .htaccess.". $passage_ligne;
        }
    }
$email_message .= $passage_ligne . "--" . $boundary . "--" . $passage_ligne; //Séparateur de fermeture

Envoi du mail

La partie la plus simple, pour laquelle vous n’avez sûrement pas besoin d’aide, l’envoi du mail.
Nous avons fait en sorte que lorsque l’envoi du mail est bien réalisé on redirige vers la page du formulaire (ici index.html).

<?php

if(mail($email_to,$email_subject, $email_message, $headers)==true){  //Envoi du mail
    header('Location: index.html'); //Redirection
}

Voilà vous savez maintenant envoyer un mail avec pièce jointe en PHP avec la fonction mail().
Si vous souhaitez télécharger directement le fichier mail.php avec seulement quelques informations à modifier (comme l’adresse mail d’envoi), vous pouvez le faire ici.

21 commentaires

  • Bonjour,
    Merci pour ce tuto qui est d’une grande aide ! 🙂

    Par contre, j’ai un soucis : l’email s’envoi mais à la réception il manque la pièce-jointe.
    J’ai essayé plusieurs choses mais rien ne change. Savez-vous d’où peut venir ce problème ?

    Merci.

  • Super tuto !!!
    Il manque juste les informations pour mettre plusieurs destinataires. A part ça, vos explications sont claires et les protections proposées sont très pertinentes. Bref, du très bon travail

    Merci encore

  • Salut Marine. J’espere que tu vas bien avec la situation actuelle.
    J’ai lu ton article sur l’envoi avec piece jointe et j’ai appris je t’en remercie.
    J’ai un soucis actuellement je voudrais envoyer un email avec un qr code pour fichier joint.
    Lors de l’execution, j’ai obtenu qu’il fallait parametrer le fichier php.ini avec notament la vatriable SMTP et smtp_port que j’ai mis a smtp.gmail.com et 465.
    Mainteant des que je recharge, je remplis le formulaire et je valide ca tourne a jamais..
    Comment resoudre ca? Il semble clair que le soucis vient de cette fonction mail()…

  • bonjour
    j’ ai testé votre formulaire. A mon avis il conviendrait bien à une administration qui demanderai des documents photo et CV.

  • bonjour pourrions nous communiquer je suis trop débutant pour y arriver snifffff je souhaitais pouvoir utiliser cette fonction sur mon formulaire mais je galère. pourriez-vous m’aider ?
    Merci d’avance

  • Super ton tuto: clair, efficace, facile à adapter à son propre code + des explications SIMPLES à comprendre.
    Cela m’a permis d’avancer plus rapidement dans mon projet!!!

  • Bonjour, super tuto, mais j’ai le même problème que Rachel, à la réception du mail je n’ai pas la pièce jointe alors que j’ai exactement les même ligne de codes que vous.

    Merci à vous

    • Il semble que le post ne transmet pas les caractéristiques du fichier.
      J’ai par exemple :
      Notice: Undefined index: file in /home/u819142381/domains/uaprr.fr/public_html/Testmail.php on line 3
      Et la suite se s’exécute pas.
      Y a-t-il une explication ?

  • Bonjour merci pour le tuto, en revanche il y a une erreur dans le formulaire il faut remplacer enctype= »multipart/mixed » par enctype= »multipart/form-data » pour que la pièce jointe puisse être upload et donc lu par $_File

  • Bonjour
    J’ai recopié à la lettre votre code mais il n’y a pas la pièce jointe… par contre le message texte est bien envoyé.

  • Bonjour, et tout d’abord merci. tout fonctionne, sauf la pièce jointe qui n’est pas joint au mail. quelqu’un aurait il une solution ?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *