HTMLメールを送信する方法

CGIテクニック集トップへ





概要

HTML書式のメールをsendmailコマンドで送信します。画像を含んだHTMLメールをスクリプトから送る方法です。

必用な処理

  1. PerlのPackage, JcodeとMIME::Base64を読み込む
  2. 宛先などの必要な設定をする
  3. メール内容を設定する
  4. メールヘッダを設定する
  5. 画像ファイルのアタッチメントを作成する
  6. メールを送信する
  7. 送信終了メッセージを表示する

サンプルコード

 use File::Basename;
 use Jcode;
 use MIME::Base64 qw(encode_base64);
 
 my $to_mail = 'tomail@dummy.com';
 my $from_mail = 'frommail@dummy.com';
 my $subject = '画像付きHTMLメール';
 my $mail_cmd = 'sendmail -t';
 
 my $image1 = "image1.jpg";
 my $image2 = "image2.jpg";
 
 my $contents = '
 <html><body>
 <h1>HTMLでのメールです。</h1>
 <center>写真を送ります。2枚あります。</center><p>
 
 写真1
 <img src="cid:image1"><br>
 
 写真2
 <img src="cid:image2"><br>
 </body>
 </html>
 ';
 
 $contents = jcode($contents)->jis;
 
 my $header;
 $header = "From: " . jcode("$from_mail")->mime_encode . "\n";
 $header .= "To: " . jcode("$to_mail")->mime_encode . "\n";
 $header .= "Subject: " . jcode($subject)->mime_encode . "\n";
 $header .= "MIME-Version: 1.0\n";
 $header .= "Content-type: multipart/related; boundary=\"boundary-here--\";type=Text/HTML;\n";
 $header .= "Content-Transfer-Encoding: 7bit\n\n";
 
 $header .= "--boundary-here--\n";
 $header .= "Content-type: Text/HTML; charset=ISO-2022-JP\n\n";
 
 my $buf;
 my $footer = "\n";
 foreach my $im ($image1,$image2){
     my ($body, $path, $ext) = fileparse($im, '\.\w+');
     my %type = (jpg => 'JPEG',
                 gif => 'GIF',
                 png => 'PNG');
     $ext =~ s/\.//;
     $footer .= "--boundary-here--\n";
     $footer .= "Content-Disposition: attachment; filename=\"$im\"\n";
     $footer .= "Content-Type: IMAGE/$type{$ext}\n";
     $footer .= "Content-ID: $body\n";
     $footer .= "Content-Transfer-Encoding: BASE64\n\n";
     open(FILE, "< $im");
     while (read(FILE, $buf, 60*57)) {
            $footer .= encode_base64($buf);
     }
     close(FILE);
 }
 
 if (open(SMAIL, "| $mail_cmd")){
        print SMAIL $header;
        print SMAIL $contents;
        print SMAIL $footer;
        close(SMAIL);
 } else {
        &error("メールコマンドが実行できません。<br>$mail_cmdが正しいか確認してください。");
 }
 
 prnit "Content-Type: text/html\n\n";
 print "送信しました。";

コードの解説

 use File::Basename;
 use Jcode;
 use MIME::Base64 qw(encode_base64);

必要なPerlのPackageをロードします。

 my $to_mail = 'tomail@dummy.com';
 my $from_mail = 'frommail@dummy.com';
 my $subject = '画像付きHTMLメール';
 my $mail_cmd = 'sendmail -t';

送り先メールアドレス、送り主メールアドレス、メールのタイトル、メールコマンドを変数に入力します。通常CGIの場合はメールアドレスやタイトルの値は送信フォームから引き渡されます。この例では簡便の為に直接変数を設定しています。 sendmailはヘッダを本文に埋め込む方式の為に-tのオプションが必要になります。

 my $image1 = "image1.jpg";
 my $image2 = "image2.jpg";

メールに添付する画像を指定します。この例では二つの画像を添付するために画像のファイル名を二つ指定しています。これらもCGIの場合は送信フォームから引き渡されます。

 my $contents = '
 <html><body>
 <h1>HTMLでのメールです。</h1>
 <center>写真を送ります。2枚あります。</center><p>
 
 写真1
 <img src="cid:image1"><br>
 
 写真2
 <img src="cid:image2"><br>
 </body>
 </html>
 ';

メールの内容を変数に入力しています。HTMLメールなのでHTMLフォームになっています。この内容もCGIの場合は送信フォームから引き渡されます。 画像のsrcの指定がファイル名ではなくIDになります。このIDが添付部分のContent-IDと一致しなければいけません。それぞれ対応した画像がこのHTMLに埋め込まれることになります。

 $contents = jcode($contents)->jis;

メール内容をJISにコード変換します。これは必ずしもJISでなくてもかまいません。Shift_JIS, EUC-JP, UTF-8などなんでもかまいませんが、ヘッダのコード指定と一致しないといけません。

 my $header;
 $header = "From: " . jcode("$from_mail")->mime_encode . "\n";
 $header .= "To: " . jcode("$to_mail")->mime_encode . "\n";
 $header .= "Subject: " . jcode($subject)->mime_encode . "\n";

ヘッダを指定します。ここでは変数に入力しています。jcode()->mime_encodeによりメールアドレスとタイトルをエンコードします。これによってアドレスやタイトルに日本語が入っている場合でも受け取り側のメールソフトで日本語が表示できるようになります。

 $header .= "MIME-Version: 1.0\n";
 $header .= "Content-type: multipart/related; boundary=\"boundary-here--\";type=Text/HTML;\n";
 $header .= "Content-Transfer-Encoding: 7bit\n\n";

ここもヘッダです。Content-typeの設定をmultipart/relatedにしているのでファイルを添付することができるようになります。boundaryの文字列がメール内容、添付ファイルのセパレーターの役目をします。セパレーターは"--"から始まり、それに続く指定した文字列になります。ここでは"boundary-here--"という文字列を指定しているので"--boundary-here--"がセパレーターになります。 最後の二回連続の改行によりヘッダの終わりを宣言できます。

 $header .= "--boundary-here--\n";
 $header .= "Content-type: Text/HTML; charset=ISO-2022-JP\n\n";

この文は実際はメール本文に含まれます。先に説明したセパレーターによりここから次のセパレーターまでが一つのブロックとなります。ここでcharsetがISO-2022-JPなので本文の文字コードがJISでないといけません。本文の文字コードがそれ以外の場合はここのcharsetをそれに合わせて適切に指定する必要があります。

 my $buf;
 my $footer = "\n";
 foreach my $im ($image1,$image2){
     my ($body, $path, $ext) = fileparse($im, '\.\w+');
     my %type = (jpg => 'JPEG',
                 gif => 'GIF',
                 png => 'PNG');
     $ext =~ s/\.//;
     $footer .= "--boundary-here--\n";
     $footer .= "Content-Disposition: attachment; filename=\"$im\"\n";
     $footer .= "Content-Type: IMAGE/$type{$ext}\n";
     $footer .= "Content-ID: $body\n";
     $footer .= "Content-Transfer-Encoding: BASE64\n\n";
     open(FILE, "< $im");
     while (read(FILE, $buf, 60*57)) {
            $footer .= encode_base64($buf);
     }
     close(FILE);
 }

ここで添付ファイルを作成します。ファイル自体はBase64でエンコードされます。二つのファイルを添付するので二回繰り返しています。エンコードの前にセパレーターとヘッダが付加しています。Content-ID:で指定してある文字列とメール本文の<img src="cid:...">の文字列が一致した所に画像が表示されます。ここではファイル名の拡張子を除いた部分をIDとして使用しています。Content-Dispositionのfilenameが一致しても本文のHTMLに画像は表示されません。Content-Transfer-Encodingでエンコード方法のBASE64も指定します。

 read(FILE, $buf, 60*57)

とあるようにサイズの大きなファイルは57バイトの倍数でエンコードします。これによってbase64の行が揃うことになります。この事はCPANのMAME::Base64のページに説明があります。

 if (open(SMAIL, "| $mail_cmd")){
        print SMAIL $header;
        print SMAIL $contents;
        print SMAIL $footer;
        close(SMAIL);
 } else {
        &error("メールコマンドが実行できません。<br>$mail_cmdが正しいか確認してください。");
 }

メールを送信します。コマンドをパイプでオープンし、ヘッダ、内容、添付ファイルをそのコマンドに送信します。コマンドがオープンできない場合にエラーを返します。

 prnit "Content-Type: text/html\n\n";
 print "送信しました。";

必要であれば送信終了ページを表示します。そのままLocationで次のページに飛ぶのもよいでしょう。

関連するCGIテクニック

テキストメールを送信する方法
Net::SMTPを使ってメールを送信する方法
Net::SMTP::TLSを使って認証付きサーバーからメールを送信する方法
JISの長文メールの文字化けの解消