首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 發送帶附件郵件完整類庫:MIME 結構解析與 SMTP 直發實戰

     編輯:PHP 相關知識

前言

PHP 內建 mail() 函數依賴本地 MTA (sendmail/postfix),且不原生支援附件。本文提供一個純 PHP 實現的 CMailFile 類,手動構建 MIME multipart/mixed 結構,支援 Base64 編碼附件、自定義檔名、MIME 類型,無需外部依賴即可發送帶附件郵件。

核心類:CMailFile

<?php
class CMailFile {
    var $subject;          // 郵件主旨
    var $addr_to;          // 收件人
    var $text_body;        // 純文字內容部份
    var $text_encoded;     // 附件編碼部份
    var $mime_headers;     // MIME 標頭
    var $mime_boundary = "--==================_846811060==_"; // 邊界分隔符 (唯一隨機字串更佳)
    var $smtp_headers;     // SMTP 標頭

    /**
     * @param string $subject    主旨
     * @param string $to         收件人
     * @param string $from       寄件人
     * @param string $msg        郵件純文字內容
     * @param string $filename   附件實體路徑
     * @param string $downfilename 附件下載顯示檔名
     * @param string $mimetype   MIME 類型 (預設 application/octet-stream)
     * @param string $mime_filename  附件在郵件中顯示的檔名 (可選)
     */
    function CMailFile($subject, $to, $from, $msg, $filename, $downfilename, 
                       $mimetype = "application/octet-stream", $mime_filename = false) {
        $this->subject = $subject;
        $this->addr_to = $to;
        $this->smtp_headers = $this->write_smtpheaders($from);
        $this->text_body = $this->write_body($msg);
        $this->text_encoded = $this->attach_file($filename, $downfilename, $mimetype, $mime_filename);
        $this->mime_headers = $this->write_mimeheaders($filename, $mime_filename);
    }

    /** 讀取檔案並 Base64 編碼 */
    function encode_file($sourcefile) {
        if (is_readable($sourcefile)) {
            $fd = fopen($sourcefile, "r");
            $contents = fread($fd, filesize($sourcefile));
            $encoded = chunk_split(base64_encode($contents)); // RFC 2045 建議每 76 字元換行
            fclose($fd);
            return $encoded;
        }
        return '';
    }

    /** 組裝附件 MIME 部份 */
    function attach_file($filename, $downfilename, $mimetype, $mime_filename) {
        $encoded = $this->encode_file($filename);
        if ($mime_filename) $filename = $mime_filename;
        $out = "--" . $this->mime_boundary . "\n";
        $out .= "Content-type: " . $mimetype . "; name=\"$filename\"\n";
        $out .= "Content-Transfer-Encoding: base64\n";
        $out .= "Content-disposition: attachment; filename=\"$downfilename\"\n\n";
        $out .= $encoded . "\n";
        $out .= "--" . $this->mime_boundary . "--\n";
        return $out;
    }

    /** 發送郵件 */
    function sendfile() {
        $headers = $this->smtp_headers . $this->mime_headers;
        $message = $this->text_body . $this->text_encoded;
        return mail($this->addr_to, $this->subject, $message, $headers);
    }

    /** 純文字內容 MIME 部份 */
    function write_body($msgtext) {
        $out = "--" . $this->mime_boundary . "\n";
        $out .= "Content-Type: text/plain; charset=\"us-ascii\"\n\n";
        $out .= $msgtext . "\n";
        return $out;
    }

    /** MIME 總標頭 */
    function write_mimeheaders($filename, $mime_filename) {
        if ($mime_filename) $filename = $mime_filename;
        $out = "MIME-version: 1.0\n";
        $out .= "Content-type: multipart/mixed; boundary=\"$this->mime_boundary\"\n";
        $out .= "Content-transfer-encoding: 7BIT\n";
        $out .= "X-attachments: $filename;\n\n";
        return $out;
    }

    /** SMTP 基本標頭 */
    function write_smtpheaders($addr_from) {
        $out = "From: $addr_from\n";
        $out .= "Reply-To: $addr_from\n";
        $out .= "X-Mailer: PHP/" . phpversion() . "\n";
        $out .= "X-Sender: $addr_from\n";
        return $out;
    }
}
?>

使用範例

<?php
require_once 'CMailFile.php';

// 參數設定
$subject = "測試帶附件郵件";
$sendto  = 'recipient@example.com';
$replyto = 'sender@example.com';
$message = "這是一封測試郵件,包含圖片附件。";
$filename = __DIR__ . '/test.jpg';        // 伺服器實體路徑
$downfilename = 'photo.jpg';              // 收件者看到的檔名
$mimetype = "image/jpeg";

$mail = new CMailFile($subject, $sendto, $replyto, $message, $filename, $downfilename, $mimetype);
if ($mail->sendfile()) {
    echo "郵件發送成功!";
} else {
    echo "郵件發送失敗,請檢查 mail() 設定與錯誤日誌。";
}
?>

進階:支援多附件與 HTML 內容

原類僅支援單一附件與純文字。實務上常需「多附件」+「HTML 郵件」。建議改用 PHPMailer 或 Symfony Mailer 等成熟庫,但若必須零依賴,可參考以下擴展思路:

  1. attach_file() 回傳陣列,sendfile() 迴圈拼接多個附件部份。
  2. write_body() 新增 text/html 部份,結構改為 multipart/alternative 包裹 text/plain + text/html,最外層為 multipart/mixed 掛載附件。
  3. 邊界字串 $mime_boundary 建議使用 md5(uniqid(time())) 動態生成,避免衝突。

常見問題排查

現象原因解決
收不到信本地無 MTA、sendmail_path 錯誤、IP 被封、進垃圾桶檢查 php.ini sendmail_path、使用 SMTP 直發庫、設定 SPF/DKIM
附件損壞/無法開啟Base64 編碼錯誤、二進位讀取模式缺失、邊界格式錯誤Windows 下 fopen 用 "rb"、確認 chunk_split、結尾 --boundary--
中文檔名亂碼RFC 2231 編碼缺失檔名參數用 =?UTF-8?B?base64?= 格式
發送超時SMTP 伺服器回應慢、防火牆阻擋 25/465/587 埠增加 max_execution_time、檢查網路連通性

總結

CMailFile 展示了 MIME 郵件構建的核心原理,適合學習或極簡環境。正式專案強烈建議採用 PHPMailer (SMTP 直發、多附件、HTML、內嵌圖片、DKIM 簽名、異步佇列) 等成熟方案,避免重複造輪子踩坑。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved