【问题标题】:phpmailer stops suddenly in case of big attachments如果附件很大,phpmailer会突然停止
【发布时间】:2019-05-19 16:49:23
【问题描述】:

问题

我有一个脚本可以向我的联系人发送简报。如果我没有附件(经过 2,000 封电子邮件证明),脚本可以正常工作。

现在,如果我使用附件,脚本也可以正常工作。但仅通过发送大约 30 封电子邮件。

测试设置

  • 要执行(循环)的 100 封电子邮件组。
  • 附件:2 个文件(共 4,6 MB)--> 脚本在 50 秒后终止(但没有错误消息)并发送了 34 封电子邮件。 (~ 156 MB)。

测试变体

  • 将 php.ini memory_limit 从 100M 更改为 500M --> 无效。发送 34 封电子邮件后仍然出错。
  • 在每个循环之后添加sleep(5) --> 无效。发送 34 封电子邮件后仍然出错。
  • 无附件:所有 100 封电子邮件都已发送(大约 30 秒)。
  • 2000 封邮件无附件:2000 封邮件全部发送(约 6 分钟)。
  • 在 php.ini 中更改 max_execution_time 无效。

假设

由于我预计会有内存问题而不是时间问题的行为。

但对每个循环中的内存 (memory_get_usage()) 的测试表明,第一个循环的内存为 1.1 MB,第 34 个循环的内存为 1.2 MB

问题

请在下面找到我的代码,但我想应该没问题。 有谁知道是什么导致了这个问题?非常感谢!

myMailer.class

class myMailer extends PHPMailer {

    public function __construct(?bool $exceptions = true) {
        $config = parse_ini_file('../../ini/config.ini', true);

        parent::__construct($exceptions);

        try {
            // Language of Errors
            $this->setLanguage('en', dirname(__FILE__) . '/../external/PHPMailer/language/');

            // Server settings
            $this->SMTPDebug  = 0;                                  // Enable verbose debug output
            $this->isSMTP();                                        // Set mailer to use SMTP
            $this->SMTPAuth   = true;                               // Enable SMTP authentication
            $this->SMTPSecure = 'ssl';                              // Enable TLS encryption, `ssl` also accepted
            $this->Port       = 465;                                // TCP port to connect to
            $this->Host       = $config['smtp_server']['host'];     // SMTP server
            $this->Username   = $config['smtp_server']['username']; // SMTP username
            $this->Password   = $config['smtp_server']['password']; // SMTP password
            $this->CharSet    ='UTF-8';                             // Set Character Set
            $this->isHTML(true);                                    // Set email format to HTML
            $this->setFrom("office@superman.com", "superman OFFICE");

        } catch (Exception $e) {
            // echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
            trigger_error("myMailer(): " . $this->ErrorInfo,E_USER_ERROR);
        }
    }

    public function setBody(string $salutation, string $receiver, string $message) : void {
        // Linebreak to <BR>
        $message = nl2br($message);

        // Create Body
        $body = file_get_contents('../../ini/email_template.html');
        $body = str_replace("{Receiver}",       $receiver,                  $body);
        $body = str_replace("{Salutation}",     $salutation,                $body);
        $body = str_replace("{Message}",        $message,                   $body);

        $this->Body = $body;
    }
}

send_emails.php

<?php
$groupID     = $_POST['id'];
$subject     = $_POST['Subject'];
$message     = $_POST['Message'];
$attachments = (key_exists('Files', $_POST)) ? $_POST['Files'] : array();

$group = new Group($groupID);

// Prepare and get HTML
$htmlGenerator  = HtmlGenerator::getInstance();
$htmlGenerator->setTitle("sending emails");
$header = $htmlGenerator->getWebbaseHtmlHeader();
$footer = $htmlGenerator->getWebbaseHtmlFooter();

echo $header;

?>
    <div class="container">
        <h1>Emailing</h1>
        <h3>Group: <?php echo $group->getTitle(); ?></h3>
        <h4>Number of members: <?php echo $group->getNumberOfMembers(); ?></h4>
        <?php

            $strScreenOutput = "
                <table class='table table-sm table-hover table-responsive-md'>
                    <thead class='thead-dark'>
                        <th scope='col'>Nr.</th>
                        <th scope='col'>Date</th>
                        <th scope='col'>Company</th>
                        <th scope='col'>Lastname</th>
                        <th scope='col'>Firstname</th>
                        <th scope='col'>Email</th>
                        <th scope='col'>Newsletter</th>
                        <th scope='col'>Result</th>
                    </thead>
                    <tbody>";

            $protocol = $strScreenOutput;

            echo $strScreenOutput;

            $i = 0;
            $members  = Contact::getAllOfGroup($groupID);
            foreach ($members as $contact) {
                $i++;
                $datRun = date('d.m.Y (H:i:s)');

                if ($contact->getNewsletter() === false) {
                    $result = "no sub.";
                } elseif ($contact->getEmail() === "" || $contact->getEmail() === null) {
                    $result = "no email";
                } else {
                    $return = $contact->sendEmail($subject, $message, $attachments);
                    $result = ($return===true) ? "ok" : $return;
                }

                // create feedback for browser
                $strScreenOutput  = "<tr>";
                $strScreenOutput .= "<th scope='row'>".str_pad($i, 4 ,'0', STR_PAD_LEFT)."</th>";
                $strScreenOutput .= "<td>{$datRun}</td>";
                $strScreenOutput .= "<td>{$contact->getCompany()}</td>";
                $strScreenOutput .= "<td>{$contact->getLastName()}</td>";
                $strScreenOutput .= "<td>{$contact->getFirstName()}</td>";
                $strScreenOutput .= "<td>{$contact->getEmail()}</td>";
                $strScreenOutput .= "<td>".(($contact->getNewsletter()) ? "yes" : "no")."</td>";
                $strScreenOutput .= "<td>{$result}</td>";
                $strScreenOutput .= "</tr>";
                echo str_pad($strScreenOutput,4096)."\n"; // Add some additional blanks to enable flushing (as some browsers suppress flushing)

                // create internal protocol
                $protocol .= $strScreenOutput . "\n";

                // Send to browser
                flush();
                ob_flush();

                // add some execution time
                set_time_limit(30);
            }

            $strScreenOutput = "</tbody>
                            </table>";
            $protocol .= $strScreenOutput;
            echo $strScreenOutput;
        ?>

        <h3>Emails successfully transmitted.</h3>
    </div>

<?php

    // send protocols
    $protocol = "<h3>Group: {$group->getTitle()}</h3>".$protocol;
    $protocol = "<div style='margin-top: 30px'>$protocol</div>";

    $internal = new Contact(1);
    $internal->sendEmail("PROTOCOL: ".$subject, $message . $protocol, $attachments);

echo $footer;

联系::sendEmail()

public  function sendEmail(string $subject, string $message, array $attachments = array()) {
    $mail = new myMailer();

    if ( is_null($this->getEmail() || $this->getEmail() == "") ) {
        return false;

    } else {
        try {
            // Compose Email
            $mail->addAddress($this->getEmail(), $this->getFirstName() . " " . $this->getLastName());
            $mail->Subject = $subject;
            $mail->setBody($this->getSalutationText(), $this->getAddress(), $message);

            foreach ($attachments as $file) {
                $uploadPath  = $_SERVER['DOCUMENT_ROOT'] . "/../../files/email_attachments/";
                $file_url = $uploadPath.$file;

                if (! is_dir($uploadPath))    { die("Folder \"$uploadPath\" does not exist.");}
                if (! file_exists($file_url)) { die("File \"$file_url\" does not exist."); }

                $mail->addAttachment($file_url);
            }

            $return = $mail->send();

            // clean up
            $mail->clearAddresses();
            $mail->clearAttachments();

        } catch (Exception $error) {
            $return = $error->getMessage();
        }

        return $return;
    }

    unset($mail);

}

【问题讨论】:

    标签: php loops phpmailer


    【解决方案1】:

    听起来确实是内存问题 - PHPMailer 在发送大型附件时对内存的效率不是很高 - 尝试在循环中回显您从 memory_get_usage() 获得的内容以确认内存消耗。

    一般来说,您的发送效率很低,因为您正在创建一个新的 PHPMailer 实例,重新处理相同的附件,并为每封邮件打开一个新的 SMTP 连接,所有这些都只需要执行一次。如何更高效地发送,请查看the mailing list example provided with PHPMailerthe wiki doc on sending to lists

    更有效地重用实例和连接可能会降低您的整体内存需求。

    【讨论】:

    • 嘿同步,感谢您的回答。我将尝试使用memory_get_usage() 监控消费...关于您的评论,脚本效率低下,是的,您是对的。但我只期望时间效率低下。因为它在一个单独的函数中,所以每次调用后都应该释放内存。记忆似乎是我的问题。 :-/
    • 我在每个循环中都返回了memory_get_usage()。第一个循环有1.1 MB,在第34 个循环中它返回一个值1.2 MB。所以这发展非常顺利。这里没有问题(至少在那个记忆方面)。
    • 尝试设置SMTPDebug = 2,这样您就可以准确地看到在 SMTP 级别发生的事情(输出会很大!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    • 1970-01-01
    • 2016-03-03
    • 2010-10-31
    相关资源
    最近更新 更多