// を使用してメールを送信する Go 言語のサンプルを提供します。//// このコードは、SMTP プロトコル(ポート 465、SSL/TLS 暗号化)を介して// Tencent Cloud SES にメール送信リクエストを行う方法を示しています。// 全体の流れは以下のステップで構成されます:// 1. 送信パラメータを準備する(送信者、受信者、Cc、Bcc、件名、本文など);// 2. RFC 822 形式でメールの Header と Body を組み立てる(Body は Base64 エンコード);// 3. tls.Dial で SMTP サーバーへの暗号化接続を確立する;// 4. PlainAuth で認証を完了する;// 5. MAIL FROM、RCPT TO、DATA などの SMTP コマンドを順次実行してメールを配信する。//// このコードを使用する前に、Tencent Cloud Email Push コンソールで以下の準備を完了してください:// - 送信ドメイン (Sender Domain) を作成し、検証する;// - 送信アドレス (Sender Address) を作成し、SMTP 送信を有効にする;// - コンソールの「SMTP パスワード」で専用パスワードを生成する// (注意: これは Tencent Cloud アカウントのパスワードではありません);// - 送信アドレスに利用可能な送信枠があることを確認する。//// 依存関係: 標準ライブラリの net/smtp と crypto/tls のみで、サードパーティパッケージは不要です。package mainimport ("crypto/tls""encoding/base64""fmt""log""net""net/smtp")// Test465 は、ポート 465 (SSL/TLS) で HTML 形式のメールを送信するデモです。//// ワークフロー:// 1. SMTP サーバーアドレス、ポート、送信アカウント、SMTP パスワードを設定する;// 2. メールヘッダー (From / To / Cc / Bcc / Subject / Content-Type など) を構築する;// 3. 本文を Base64 エンコードしてヘッダーの後に追加する// (ヘッダーと本文の間には空行が必要);// 4. smtp.PlainAuth を使用して PLAIN 認証オブジェクトを作成する;// 5. SendMailWithTLS を呼び出して TLS 接続経由でメールを配信する。//// 戻り値: 成功時は nil を返し、失敗時は具体的なエラーを返します。func Test465() error {// SMTP サーバーアドレス。Tencent Cloud Email Push では smtp.qcloudmail.com で固定です。host := "smtp.qcloudmail.com"// SMTP ポート: 465 は SSL/TLS 暗号化接続を意味します;// STARTTLS を使用したい場合はポート 25 に変更し、コード内の tls.Dial も// それに応じて net.Dial + StartTLS に変更する必要があります。port := 465// email: コンソールで作成・検証済みの送信アドレス (Sender Address)// SMTP パスワードを所有するアカウントと一致する必要があります。email := "abc@cd.com"// password: コンソールの「SMTP 設定」ページでこの送信アドレス用に// 個別に生成される SMTP パスワードです。// Tencent Cloud のログインパスワードでも API キーでもありません。password := "****"// 受信者アドレス (必須、最低1つ)toEmail := "test@test123.com"// Cc アドレス (任意)ccEmail := "cc@test123.com"// Bcc アドレス (任意)bccEmail := "bcc@test123.com"// メールヘッダーを構築します。SMTP プロトコルでは、ヘッダーフィールドは// "Key: Value\\r\\n" 形式で記述する必要があり、すべてのヘッダーの後には// 空行 ("\\r\\n") が必要で、その後に本文が続きます。header := make(map[string]string)// "From" は "表示名 <送信アドレス>" の形式で書くことが推奨されます。// 表示名は受信トレイで送信者のニックネームとして表示されます。header["From"] = "test " + "<" + email + ">"header["To"] = toEmailheader["Cc"] = ccEmailheader["Bcc"] = bccEmail// メールの件名。非 ASCII 文字を含む場合は RFC 2047 エンコーディング// (=?UTF-8?B?...?=) が推奨されます。本サンプルは純粋な ASCII のため// エンコーディングは適用していません。header["Subject"] = "test subject"// Content-Type は本文が HTML であることを指定し、文字セットを UTF-8 として宣言します。header["Content-Type"] = "text/html; charset=UTF-8"// Content-Transfer-Encoding は本文の転送エンコーディングを指定します。// base64 を使用することで、SMTP 転送中に非 ASCII 文字や特殊文字が// 切り詰められたり破損したりするのを防ぎます。header["Content-Transfer-Encoding"] = "base64"// HTML 本文のサンプルbody := "<!DOCTYPE html>\\n<html>\\n<head>\\n<meta charset=\\"utf-8\\">\\n<title>hello world</title>\\n</head>\\n<body>\\n " +"<h1>私の最初の見出し</h1>\\n <p>私の最初の段落です。</p>\\n</body>\\n</html>"// プレーンテキストメールを送信したい場合は、上記の HTML 設定をコメントアウトし、// 以下の2行を有効にしてください://header["Content-Type"] = "text/plain; charset=UTF-8"//body := "test body"// 最終的な SMTP メッセージを組み立てます: まず各ヘッダーを "Key: Value\\r\\n"// に連結し、次に区切りとして空行を追加し、最後に Base64 エンコードされた// 本文を付加します。message := ""for k, v := range header {message += fmt.Sprintf("%s: %s\\r\\n", k, v)}message += "\\r\\n" + base64.StdEncoding.EncodeToString([]byte(body))// PLAIN 認証オブジェクトを作成します。// 第1引数 "identity" は通常空、第2引数はユーザー名 (送信アドレス)、// 第3引数は SMTP パスワード、第4引数は host (サーバー側の検証に使用) です。auth := smtp.PlainAuth("",email,password,host,)// TLS 版送信関数を呼び出し、サーバーアドレス、認証オブジェクト、// 送信者、受信者リスト、生メッセージを渡します。// 注意: "to" リストは SMTP の RCPT TO コマンドに使用され、実際にどのメール// ボックスにメールが配信されるかを決定します。Cc/Bcc 受信者にも実際にメール// を受信させたい場合は、ccEmail / bccEmail もこのリストに追加する必要があります。err := SendMailWithTLS(fmt.Sprintf("%s:%d", host, port),auth,email,[]string{toEmail},[]byte(message),)if err != nil {fmt.Println("Send email error:", err)} else {fmt.Println("Send mail success!")}return err}// Dial は、SMTP サーバーへの TLS 暗号化接続を確立し、使用可能な// *smtp.Client を返します。//// パラメータ:// - addr: "host:port" 形式のサーバーアドレス。例: "smtp.qcloudmail.com:465"。//// 注意:// - 標準ライブラリの smtp.Dial はデフォルトで平文接続を使用します。// ポート 465 では、まず TLS チャネルを確立してから SMTP クライアントに// ラップする必要があるため、この関数では net.Dial の代わりに tls.Dial を使用します。// - 第2引数は *tls.Config で、nil を渡すとデフォルト設定(サーバー証明書を検証する)// を使用します。// 証明書の検証をスキップしたい場合(本番環境では非推奨)、// &tls.Config{InsecureSkipVerify: true} を渡してください。func Dial(addr string) (*smtp.Client, error) {conn, err := tls.Dial("tcp", addr, nil)if err != nil {log.Println("tls.Dial Error:", err)return nil, err}// smtp.NewClient は EHLO/HELO ハンドシェイクのために host (ポートを含まない) を必要とします。host, _, _ := net.SplitHostPort(addr)return smtp.NewClient(conn, host)}// SendMailWithTLS は、TLS 暗号化された SMTP 接続を介して完全なメール配信を実行します。//// パラメータ:// - addr: "host:port" 形式の SMTP サーバーアドレス。// - auth: SMTP 認証オブジェクト。smtp.PlainAuth で作成可能;// 認証が不要な場合は nil を渡します。// - from: MAIL FROM コマンドで使用される送信者アドレス。// 認証されたアカウントと一致している必要があります。// - to: RCPT TO コマンドで使用される受信者リスト。// To、Cc、Bcc のすべての実際の受信者を含みます。// - msg: 完全なメールメッセージ (Header + 空行 + Body)。RFC 822 形式である必要があります。//// ステップ:// 1. Dial で TLS + SMTP 接続を確立する;// 2. サーバーが AUTH 拡張をサポートしていれば、認証を実行する;// 3. MAIL FROM、RCPT TO、DATA コマンドを順次実行する;// 4. メール本文を書き込み、データストリームを閉じる;// 5. QUIT コマンドを呼び出してセッションを正常に終了する。//// いずれかのステップが失敗した場合、対応するエラーが直ちに返されます。func SendMailWithTLS(addr string, auth smtp.Auth, from string,to []string, msg []byte) (err error) {// 1. TLS 接続を確立し、SMTP クライアントを取得します。c, err := Dial(addr)if err != nil {log.Println("Create smtp client error:", err)return err}// 接続リークを避けるため、最終的に接続が閉じられることを保証します。defer c.Close()// 2. auth が指定され、サーバーが EHLO で AUTH 拡張をアドバタイズしている場合、// 認証を実行します。if auth != nil {if ok, _ := c.Extension("AUTH"); ok {if err = c.Auth(auth); err != nil {log.Println("Error during AUTH", err)return err}}}// 3. MAIL FROM コマンドを送信して、サーバーに送信者を伝えます。if err = c.Mail(from); err != nil {return err}// 4. 各受信者に対して RCPT TO コマンドを送信します。for _, addr := range to {if err = c.Rcpt(addr); err != nil {return err}}// 5. DATA コマンドを送信し、メール本文 (Header + Body) を書き込むための writer を取得します。w, err := c.Data()if err != nil {return err}_, err = w.Write(msg)if err != nil {return err}// writer を閉じて本文入力の終了を示します// (SMTP プロトコルでは終了は "\\r\\n.\\r\\n" で示されます)。err = w.Close()if err != nil {return err}// 6. QUIT コマンドを送信して、サーバーとのセッションを正常に終了します。return c.Quit()}// Main はエントリーポイントで、Test465 を直接呼び出して送信をトリガーします。// 実際のビジネス統合では、送信パラメータ (アカウント、パスワード、受信者など) を// 設定や関数引数に切り出し、Test465 のエラーに対してより細かい処理と// リトライを実装することを推奨します。func main() {Test465()}
フィードバック