AWS SES SDK 发送带附件的电子邮件

AWS SES SDK send email with attachments

我正在使用 official AWS Golang SDK 与 SES 集成,但找不到有关如何将某些附件(在代码中表示为 []byte 的 pdf 文件)添加到电子邮件的任何信息。

你能帮帮我吗?

当前的电子邮件发送代码如下所示:

sesEmailInput := &ses.SendEmailInput{
    Destination: &ses.Destination{
        ToAddresses: []*string{aws.String("To address")},
    },
    Message: &ses.Message{
        Subject: &ses.Content{
            Data: aws.String("Some text"),
        },
        Body: &ses.Body{
            Html: &ses.Content{
                Data: aws.String("Some Text"),
            },
        },
    },
    Source: aws.String("From address"),
    ReplyToAddresses: []*string{
        aws.String("From address"),
    },
}
if _, err := s.sesSession.SendEmail(sesEmailInput); err != nil {
    return err
}

要发送附件,请使用 SendRawEmail API 而不是 SendEmail。 AWS 文档通常将此称为构建 'raw message' 而不是明确指出如何发送附件。

例子

来自 AWS SDK for Go API 参考,链接如下:

params := &ses.SendRawEmailInput{
    RawMessage: &ses.RawMessage{ // Required
        Data: []byte("PAYLOAD"), // Required
    },
    ConfigurationSetName: aws.String("ConfigurationSetName"),
    Destinations: []*string{
        aws.String("Address"), // Required
        // More values...
    },
    FromArn:       aws.String("AmazonResourceName"),
    ReturnPathArn: aws.String("AmazonResourceName"),
    Source:        aws.String("Address"),
    SourceArn:     aws.String("AmazonResourceName"),
    Tags: []*ses.MessageTag{
        { // Required
            Name:  aws.String("MessageTagName"),  // Required
            Value: aws.String("MessageTagValue"), // Required
        },
        // More values...
    },
}
resp, err := svc.SendRawEmail(params)

进一步阅读

在浪费了几个小时之后,您应该了解有关原始电子邮件的事情。

遵循这样的结构:

boundary=boundary_name \n\n  (create a boundary with boundary= and \n\n)
--boundary_name              (start a boundary with --)
  Header                     (add headers with Content-Type:)
                             (one blank line with \n)
  html or text or file       (add your content)
                             (one blank line with \n)
--boundary_name-- \n\n       (close boundary with -- -- and \n\n)
  

带有 PHP 代码的示例:

$boundary = md5(time());

$html = "<h1>E-mail Test</h1>";
$html .= "<img src=\"cid:{$attachment['filename']}\">"; // Content-ID

$text = "You need HTML enabled";


$raw = '';
$raw .= "From:{$from}\n";
$raw .= "To:{$to}\n";
$raw .= "Subject:{$subject}\n";
$raw .= "MIME-Version: 1.0\n";
$raw .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\n\n";
$raw .= "--{$boundary}\n";

    $raw .= "Content-Type: multipart/alternative; boundary=\"sub_{$boundary}\"\n\n";
    $raw .= "--sub_{$boundary}\n";
        $raw .= "Content-Type: text/plain; charset=\"UTF-8\"\n";
        $raw .= "\n";
        $raw .= "{$text}\n";
        $raw .= "\n";
    $raw .= "--sub_{$boundary}\n";
        $raw .= "Content-Type: text/html; charset=\"UTF-8\"\n";
        $raw .= "Content-Disposition: inline\n";
        $raw .= "\n";
        $raw .= "{$html}\n";
        $raw .= "\n";
    $raw .= "--sub_{$boundary}--\n\n";

foreach ($this->email->getFiles() as $attachment) {
    $raw .= "--{$boundary}\n";
        $raw .= "Content-Type:{$attachment['mimetype']}; name=\"{$attachment['filename']}\"\n"; // name
        $raw .= "Content-Transfer-Encoding:base64\n";
        $raw .= "Content-Disposition:attachment;filename=\"{$attachment['filename']}\"\n"; // filename
        #$raw .= "Content-Disposition:inline;name={$file_name}\n";
        $raw .= "Content-ID:<{$attachment['filename']}>\n";
        $raw .= "\n";
        $raw .= base64_encode($attachment['content']) . "\n";
        $raw .= "\n";
}
$raw .= "--{$boundary}--\n\n";

最终原始消息示例:

From:Name From<name@from.com>
To:email@to.com,second@to.com
Subject:Your subject
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="b4f53df7c26ae6945f29c42d5c2bb55f"

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: multipart/alternative; boundary="sub_b4f53df7c26ae6945f29c42d5c2bb55f"

--sub_b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: text/plain; charset="UTF-8"

You need HTML enabled

--sub_b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: text/html; charset="UTF-8"
Content-Disposition: inline

<h1>E-mail Test</h1>
<img src="cid:qrcode.png"> // <-- from Header Content-ID:<name>

--sub_b4f53df7c26ae6945f29c42d5c2bb55f--

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type:image/png; name="qrcode.png"
Content-Transfer-Encoding:base64
Content-Disposition:attachment;filename="qrcode.png"
Content-ID:<qrcode.png> // you can use <img src="cid:qrcode.png"> in html

iVBORw0K== // base64encoded file contents

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type:image/png; name="another_file.png"
Content-Transfer-Encoding:base64
Content-Disposition:attachment;filename="another_file.png"
Content-ID:<another_file.png>

iVBORw0KGg== // base64encoded file contents

--b4f53df7c26ae6945f29c42d5c2bb55f--

别忘了,在Headers之后,content前后各空一行

对于PHP AWS SDK

try {
    $client = new SesClient($options);
    $result = $client->sendRawEmail([
        'RawMessage' => [
            'Data' => $raw
        ],
    ]);
    echo $result['MessageId'];
}
catch (AwsException $e) {
    echo $e->getAwsErrorMessage();       
    return false;
}

SES 原始消息必须 base64 编码。因此,您需要获取文件内容作为缓冲区并将其编码为 base64 字符串附件。此外,您不需要为原始消息数据创建新缓冲区,因为它已经接受字符串数据类型。

可选:您也可以省略 Destinations 参数,因为您已经提供了 To 原始消息数据中的字段。 (您还可以提供 CcBcc 字段)

你可以试试这个,例如:

const sendAttachmentEmail = async () => {
  // Yo can use by this way
  const DATA = await workbook.writeToBuffer(); // This workbook is excel generated
  // Or this way
  const DATA = fs.readFileSync("files/demo-invoice.xlsx"); // This is excel file in files folder

  const SOURCE_EMAIL = "xyz@gmail.com";
  const TO_EMAIL = "abc@gmail.com";

  let ses_mail = "From: 'AWS SES Attchament Configuration' <" + SOURCE_EMAIL + ">\n";
  ses_mail += "To: " + TO_EMAIL + "\n";
  ses_mail += "Subject: AWS SES Attachment Example\n";
  ses_mail += "MIME-Version: 1.0\n";
  ses_mail += "Content-Type: multipart/mixed; boundary=\"NextPart\"\n\n";
  ses_mail += "--NextPart\n";
  ses_mail += "Content-Type: text/html\n\n";
  ses_mail += "This is the body of the email.\n\n";
  ses_mail += "--NextPart\n";
  ses_mail += "Content-Type: application/octet-stream; name=\"demo-invoice.xlsx\"\n";
  ses_mail += "Content-Transfer-Encoding: base64\n";
  ses_mail += "Content-Disposition: attachment\n\n";
  ses_mail += data.toString("base64").replace(/([^[=10=]]{76})/g, "\n") + "\n\n";
  ses_mail += "--NextPart--";

  const params = {
    RawMessage: {
      Data: ses_mail
    },
    Source: "'AWS SES Attchament Configuration' <" + SOURCE_EMAIL + ">'"
  };

  return new Promise((resolve, reject) => {
    ses.sendRawEmail(params, (err) => {
      if (err) {
        return reject(err);
      }
      return resolve();
    });
  });
};

注意: /([^\0]{76})/ 正则表达式替换会打断您的长行,以确保邮件服务器不会抱怨消息行被有编码附件时太长,这可能会导致瞬时反弹。