【问题标题】:PHPMailer multiple attachments sends email but no filesPHPMailer多个附件发送电子邮件但没有文件
【发布时间】:2020-01-25 21:07:58
【问题描述】:

我似乎已经尝试了我能找到的关于这个特定主题的所有 StackOverflow(并且有很多),但大多数人忘记了表单标签中的 enctype 或其他东西。我仍在收到电子邮件,只是没有附加任何文件。

当我提交表单时,我的$_FILES 数组似乎没有被填满。下面的代码使用提交的字段数据完美地发送 html 电子邮件,但没有附件。

这是表单提交输入数据但没有附件时日志中的最新错误:

[24-Jan-2020 22:06:33 UTC] PHP Warning: count(): Parameter must be an array or an object that implements Countable in *******/public_html/rtform/contact.php on line 89

我在下面的代码中指出了第 89 行。

这是我的表单的开头和结尾,底部是我的文件输入(表单的其余部分太长,无法在此处包含整个内容:

<form id="contact-form" method="POST" action="contact.php" role="form" enctype="multipart/form-data">
    <div class="controls">

        <!-- the middle of the form here -->

        <div class="row">
            <h3 class="form_title">Upload Additional Supporting Documents</h3>
            <div class="col-lg-12">
                <label for="attachments[]">Select one or more files:
                    <input name="attachments[]" type="file" multiple="multiple">
                </label>
            </div>
        </div>

        <hr/>

        <div class="form-group">
            <div class="g-recaptcha" data-sitekey="**************************************" data-callback="verifyRecaptchaCallback" data-expired-callback="expiredRecaptchaCallback"></div>
            <input class="form-control d-none" data-recaptcha="true" required data-error="Please complete the Captcha">
            <div class="help-block with-errors"></div>
        </div>

        <p><span class="red">Fields marked with * denotes a required field.</span></p>
        <input type="submit" class="btn btn-success btn-send" value="Submit Order">

        <div class="messages"></div>

    </div><!-- end .controls -->

</form>

我的表单处理 PHP 文件很长,但可能需要包含所有内容,所以:

<?php
// error_reporting(E_ALL); ini_set('display_errors', 1);

// Import PHPMailer classes into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'includes/phpmailer/src/Exception.php';
require 'includes/phpmailer/src/PHPMailer.php';

// Require ReCaptcha class
require('recaptcha-master/src/autoload.php');

// ReCaptch Secret
$recaptchaSecret = *******************************;

/*******************************************************
 * 
 *   Here I have a long array with all of the input names and variables which I use for 
 *   the logic below for my html email template(which works great). Shouldn't be necessary 
 *   for this problem, so I'm excluding it.
 *
 *******************************************************/

// Instantiation and passing `true` enables exceptions
$mail = new PHPMailer(true);

try {
    if ( !empty($_POST) ) {
        $msg = ''; // To be used for dynamic messaging

        // ReCaptcha
        // Validate the ReCaptcha, if something is wrong, we throw an Exception,
        // i.e. code stops executing and goes to catch() block
        if (!isset($_POST['g-recaptcha-response'])) {
            throw new \Exception('ReCaptcha is not set.');
        }
        $recaptcha = new \ReCaptcha\ReCaptcha($recaptchaSecret, new \ReCaptcha\RequestMethod\CurlPost());
        // we validate the ReCaptcha field together with the user's IP address
        $response = $recaptcha->verify($_POST['g-recaptcha-response'], $_SERVER['REMOTE_ADDR']);
        if (!$response->isSuccess()) {
            throw new \Exception('ReCaptcha was not validated.');
        }

        $requestorEmail = "";
        //Make sure the address they provided is valid before trying to use it
        if (array_key_exists('requestoremail', $_POST) && PHPMailer::validateAddress($_POST['requestoremail'])) {
            $requestorEmail = $_POST['requestoremail'];
        } else {
            $msg = 'Error: invalid requestor email address provided';
            throw new \Exception('Requestor email, ' . $requestorEmail . ', is not a valid email address.');
        }

        //Recipients
        $mail->setFrom('senderemail@email.com', 'Sender');
        $mail->addAddress('recipientemail@email.com', 'Recipient');    // Add a recipient
        $mail->addAddress($requestorEmail, 'Requestor');               // Name is optional
        $mail->addReplyTo('replytoemail@email.com', 'Reply To Email');

        /*************************************
        *
        *   The attachment-related stuff:
        *
        *************************************/

        $maxSize = 2 * 1024 * 1024; // 2 MB
        $fileTypes = array('text/plain', 'application/pdf', 'application/msword', 'application/rtf', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); // allowed mime-types

        // Passing these variables to my jQuery ajax request at the bottom of this file to try to get some helpful info
        $theFiles = $_FILES;        
        $filesCount = count($_FILES['attachments']['tmp_name']);

        if (isset($_FILES)) {

            //Attach multiple files one by one

            //******************** The line below is line 89 ********************/
            for ($ct = 0; $ct < count($_FILES['attachments']['tmp_name']); $ct++) {
                $uploadfile = tempnam(sys_get_temp_dir(), hash('sha256', $_FILES['attachments']['name'][$ct]));
                $filename = $_FILES['attachments']['name'][$ct];
                $filesize = $_FILES['attachments']['size'][$ct];
                $filemime = mime_content_type($uploadfile);

                // Check $_FILES['upfile']['error'] value.
                switch ($_FILES['attachments']['error'][$ct]) {
                    case UPLOAD_ERR_OK:
                        break;
                    case UPLOAD_ERR_NO_FILE:
                        throw new RuntimeException('No file sent.');
                    case UPLOAD_ERR_INI_SIZE:
                    case UPLOAD_ERR_FORM_SIZE:
                        throw new RuntimeException('Exceeded filesize limit.');
                    default:
                        throw new RuntimeException('Unknown errors.');
                }

                try {
                    if (in_array($filemime, $fileTypes)){
                        if ($filesize <= $maxSize) {
                            if (move_uploaded_file($_FILES['attachments']['tmp_name'][$ct], $uploadfile)) {
                                $mail->addAttachment($uploadfile, $filename);
                            } else {
                                $msg .= 'Failed to move file to ' . $uploadfile;
                                throw new \Exception('Failed to move file to ' . $uploadfile);
                            }
                        }
                        else {
                            $msg = "File[s] are too large. They must each be less than or equal to 2MB.";
                            throw new \Exception('File[s] are too large. They must each be less than or equal to 2MB.');
                        }
                    }
                    else {
                        $msg = "File[s] must only be of these types: .txt, .rtf, .pdf, .doc, .docx. Please try again.";
                        throw new \Exception('File[s] must only be of these types: .txt, .rtf, .pdf, .doc, .docx. Please try again.');
                    }
                }
                catch (Exception $e) {
                    $responseArray = array('type' => 'danger', 'message' => $msg); // or $e->getMessage()
                }
            }

        }
        else {
            $msg = "No file added.";
            throw new \Exception('No file added.');
        }

        // Get template file contents
        $template = file_get_contents("email-template.html");

/*************************************************************************
 *
 *   This is constructing my email html template with input data
 *
**************************************************************************/

        // Loop $_POST content, compare to $fields array, and include/replace if not empty
        foreach($_POST as $key => $value) {

            // Create a generic row to use for each 
            $value_row = "<li>$fields[$key]: $value</li>";

            if ( !empty( $value ) ) {
                // Check each group's first member and insert/replace appropriate title string
                if( $key == "casenumber" ) {
                    $value_row = "<h2>General Information</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "client-fullname" ) {
                    $value_row = "</ul><h2>Client/Applicant</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "employer-fullname" ) {
                    $value_row = "</ul><h2>Employer/Insured</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "requestor-attorney" ) {
                    $value_row = "</ul><h2>Requestor</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "billingcarrier" ) {
                    $value_row = "</ul><h2>Billing Information</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "caseplantiff" ) {
                    $value_row = "</ul><h2>Case Caption</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "oclattorneyname" ) {
                    $value_row = "</ul><h2>Opposing Counsel</h2>\n<ul style='list-style:none;'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location1facility" ) {
                    $value_row = "</ul><h2>Delivery Instructions</h2><h3>Location 1</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location2facility" ) {
                    $value_row = "</ul><h3>Location 2</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }    
                elseif( $key == "location3facility" ) {
                    $value_row = "</ul><h3>Location 3</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location4facility" ) {
                    $value_row = "</ul><h3>Location 4</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location5facility" ) {
                    $value_row = "</ul><h3>Location 5</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location6facility" ) {
                    $value_row = "</ul><h3>Location 6</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location7facility" ) {
                    $value_row = "</ul><h3>Location 7</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location8facility" ) {
                    $value_row = "</ul><h3>Location 8</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location9facility" ) {
                    $value_row = "</ul><h3>Location 9</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                elseif( $key == "location10facility" ) {
                    $value_row = "</ul><h3>Location 10</h3>\n<ul style='list-style:none;' class='location-list'><li>$fields[$key]: $value</li>";
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
                else {
                    $template = str_replace('{{ '.$key.' }}', $value_row, $template);
                }
            }
            else {
                $template = str_replace('{{ '.$key.' }}', '', $template);
            }
        }

        // Content
        $mail->isHTML(true); // Set email format to HTML
        $mail->Subject = 'Order Form Submission from Real Time Records Website';
        $mail->Body    = $template;

        $mail->send();
        // Generic success message
        $msg = 'Your order has been received successfully. We will contact you shortly. Please contact us if you have any questions.';
        $responseArray = array('type' => 'success', 'message' => $msg, 'files' => json_encode($theFiles), 'number-of-files' => "$filesCount");
    } // end if( !empty( $_POST ) )
} catch (Exception $e) {
    // Generic error message
    $msg = 'There was an error while submitting the form. Please try again later';
    $responseArray = array('type' => 'danger', 'message' => $msg); // or $e->getMessage()
    // echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}

// This encodes the output message(with some of those attachment details) for the jQuery ajax request to pickup
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    $encoded = json_encode($responseArray);

    header('Content-Type: application/json');

    echo $encoded;
} else {
    echo $responseArray['message'];
}

我的 jQuery 确实只是显示传递的错误或成功消息,虽然我已经从 $_FILES 传递了一些信息给它以查看它是否被填充,但事实并非如此。

当发出ajax请求时,我在响应中看到的与附件相关的接收数据是:

files = '[]', 'number-of-files' = '0'

任何有 PHPMailer 经验的人都可以看看我做错了什么吗?

编辑 x2

这是我更新的 jQuery,根据下面 Synchro 的回答。它现在从表单中获取文件,并将多个文件正确附加到 FormData 对象,但 php $_FILES 对象仅计算/显示一个文件(当有两个文件时)并且不会将任何文件附加到已发送的电子邮件:

$(function () {

    window.verifyRecaptchaCallback = function (response) {
        $('input[data-recaptcha]').val(response).trigger('change');
    }

    window.expiredRecaptchaCallback = function () {
        $('input[data-recaptcha]').val("").trigger('change');
    }

    $('#contact-form').validator();

    $('#contact-form').on('submit', function (e) {
        if (!e.isDefaultPrevented()) {
            var url = "contact.php";

            const theForm = document.getElementById('contact-form');
            var fd = new FormData(theForm);
            var files = $("#file_attachments").prop('files');

            for (var i = 0; i < files.length; i++) {
              fd.append("attachments", files[i]);
              console.log("File #"+(i+1)+" attached: "+files[i]);
            }

            $.ajax({
                type: "POST",
                url: url,
                data: fd,
                processData: false,
                contentType: false,
                success: function(data) {
                    var messageAlert = 'alert-' + data.type;
                    var messageText = data.message;
                    var messageFiles = data.files;
                    var messageFilesCount = data.numberOfFiles

                    var alertBox = '<div class="alert ' + messageAlert + ' alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + messageText + '</div><div>Number of Files: ' + messageFilesCount + ' - ' + messageFiles + '</div>';
                    if (messageAlert && messageText) {
                        $('#contact-form').find('.messages').html(alertBox);
                        $('#contact-form')[0].reset();
                        grecaptcha.reset();
                    }
                },
                error: function(data) {
                    //this is going to happen when you send something different from a 200 OK HTTP
                    alert('Ooops, something happened: ' + data.message);
                }
            });
            return false;
        }
    })
});

【问题讨论】:

  • "当 ajax 请求发出时" - 什么 ajax 请求?您发布的代码中没有JS! Are file uploads enabled 在您的 PHP 安装中?您是否将其与 PHPMailer 提供的 send multiple file uploads example 进行了比较?
  • @Synchro - 我已经包含了我的 jQuery,尽管它似乎与手头的问题无关。是的,此安装中启用了文件上传,最大文件大小为 200mb,最大文件上传量为 20。是的,我直接从 PHPMailer 示例中获取了这个多文件上传 sn-p。
  • 好的。如果您的 $_FILES 超级全局为空,则表示系统配置而不是脚本问题。
  • 很高兴知道。知道具体是什么吗?

标签: php phpmailer attachment email-attachments


【解决方案1】:

你的 JS 值得问...

你正在这样做:

data: $(this).serialize(),

这不包括文件附件。您需要使用the JS FormData class 并关闭一些jQuery 功能。

首先,给文件输入一个id,这样你就可以更容易地定位它(你的label标签也应该定位这个id,而不是name属性):

<input name="attachments[]" id="attachments" type="file" multiple="multiple">

然后更改您的 ajax 代码以将表单数据放入 FormData 对象,然后将文件元素添加到其中:

var fd = new FormData('contact-form');
var files = $("#attachments").get(0).files;

for (var i = 0; i < files.length; i++) {
  fd.append("attachments", files[i]);
}
$.ajax({
  type: "POST",
  url: url,
  data: fd,
  processData: false,
  contentType: false,
  success: function (data) {
    var messageAlert = 'alert-' + data.type;
    var messageText = data.message;

    var alertBox = '<div class="alert ' + messageAlert + ' alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>' + messageText + '</div>';
    if (messageAlert && messageText) {
      $('#contact-form').find('.messages').html(alertBox);
      $('#contact-form')[0].reset();
      grecaptcha.reset();
    }
  },
  error: function (data) {
    //this is going to happen when you send something different from a 200 OK HTTP
    alert('Ooops, something happened: ' + data.message);
  }
});

注意:此方法在 Internet Explorer 10 之前的版本中不起作用。

【讨论】:

  • 不幸的是,这一切只是将$_FILES 信息放入 ajax 请求中,而不是我的问题。我需要 PHPMailer 脚本将文件正确附加到正在发送的电子邮件中。这就是 jQuery 不相关的原因。
  • 是的,但是您无法将文件放入 PHPMailer 的原因是您的 $_FILES 数组是空的,并且它是空的,因为您在 ajax 提交中没有使用 FormData,所以没有文件正在上传...
  • 还是不行。已发送的电子邮件中没有附件。我在 jQuery 中添加了FormData,但现在我什至没有在 Inspector 的 Network 选项卡中看到响应对象。 jQuery 仅用于在提交后显示成功/错误消息。 POST 的表单数据与 php 有什么关系?我真的很好奇。我的印象是 jQuery 与实际提交数据和发送电子邮件无关
  • 那你说$('#contact-form').on('submit', function (e) {会发生什么? jQuery 正在处理整个表单提交,包括附件——这就是 ajax!
  • 感谢您的帮助。对困惑感到抱歉。在您的回答的帮助下弄清楚了。
猜你喜欢
  • 2014-02-14
  • 2019-12-23
  • 1970-01-01
  • 2016-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多