Java中集成AWS SES发送电子邮件——深入解析与SDK v2实践

引言

在上一篇博客中,我们详细探讨了AWS Simple Email Service(SES)的入门知识和配置步骤。本篇博客将聚焦于如何在Java应用程序中集成AWS SES SDK v2,以实现电子邮件的发送功能。我们将逐步引导您完成必要的设置、编写代码,并讨论如何优化发送过程。

一、准备Java开发环境

首先,确保您的开发环境中已安装了Java开发工具包(JDK)。对于AWS SDK v2,推荐使用JDK 8或更高版本。您还需要配置构建工具,如Maven或Gradle,以简化依赖管理和项目构建过程。

二、添加AWS SDK v2依赖

在您的Java项目中,需要引入AWS SDK v2的依赖。以Maven为例,在项目的pom.xml文件中添加以下依赖项:

<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>ses</artifactId>
        <version>2.x.x</version> <!-- 替换为最新的SDK版本 -->
    </dependency>
</dependencies>

对于Gradle用户,在build.gradle文件中添加类似的依赖项。

三、配置AWS凭证

在使用AWS SDK v2之前,您需要配置AWS凭证。有几种方法可以实现这一点,其中最常用的是使用环境变量或AWS凭证文件。

  1. 环境变量:设置AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY环境变量,分别包含您的AWS访问密钥和秘密访问密钥。
  2. AWS凭证文件:在您的主目录下创建一个名为.aws/credentials的文件,并添加以下内容:
[default]
aws_access_key_id = 您的访问密钥
aws_secret_access_key = 您的秘密访问密钥

确保将占位符替换为您的实际凭证。

四、编写Java代码发送电子邮件

现在,我们可以开始编写Java代码来发送电子邮件了。以下是一个简单的示例,演示了如何使用AWS SDK v2发送一封文本电子邮件:

import com.alibaba.fastjson.JSON;
import com.linzi.pitpat.base.constants.RedisConstants;
import com.linzi.pitpat.base.enums.DingTalkEnum;
import com.linzi.pitpat.base.utils.DingTalkUtils;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.api.errors.ServiceUnavailableException;
import org.slf4j.MDC;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ses.SesClient;
import software.amazon.awssdk.services.ses.model.*;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author xx
 * @date 2024/2/27 14:55
 */
@Component
@Slf4j
public class EmailSender {
    private static final String ACCESS_KEY = "xx";
    private static final String SECRET_KEY = "xx";
    private static final Region REGION = Region.US_WEST_2; // e.g., "us-west-2"
    private static final String FROM_ADDRESS = "xx@xx.com";
    private static final String configurationSetName = "xx";//配置集名称
    private static final int MAX_RETRIES = 3; // 最大重试次数
    private static final int AWS_SES_SEND_QUOTA_TIME = 5 * 60;
    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor executor;
    @Resource
    private RedisTemplate redisTemplate;

    private SesClient sesClient;

    public EmailSender() {
        this.sesClient = SesClient.builder()
                .region(REGION)
                .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(ACCESS_KEY, SECRET_KEY)))
                .build();
    }

    /**
     * 发送电子邮件
     *
     * @param toAddresses    接收者电子邮件地址列表
     * @param subject        邮件主题
     * @param bodyHtml       邮件HTML正文
     * @param bodyText       邮件文本正文(可选)
     * @return SendEmailResponse 响应对象
     */
    public SendEmailResponse sendEmail(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
        try {
            SendEmailRequest sendEmailRequest = createSendEmailRequest(toAddresses,subject,bodyHtml,bodyText);
            SendEmailResponse sendEmailResponse = null;
            int retryCount = 0;
            while (retryCount < MAX_RETRIES) {
                try {
                    sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
                    log.info("sendEmail res={}", sendEmailResponse);
                    break;
                } catch (SdkException e) {
                    if (!(e.getCause() instanceof ServiceUnavailableException)) {
                        throw e;
                    }
                    log.info("Retrying due to service unavailable", e);
                    retryCount++;
                }
            }
            return sendEmailResponse;
        } catch (SdkException e) {
            handleSendEmailException(e,toAddresses);
            close();
            throw e;
        }
    }

    /**
     * 异步发送电子邮件
     *
     * @param toAddresses    接收者电子邮件地址列表
     * @param subject        邮件主题
     * @param bodyHtml       邮件HTML正文
     * @param bodyText       邮件文本正文(可选)
     * @return SendEmailResponse 响应对象
     */
    public Future<Boolean> sendEmailAsync(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
        SendEmailRequest sendEmailRequest = createSendEmailRequest(toAddresses,subject,bodyHtml,bodyText);
        return executor.submit(() -> {
            try {
                int retryCount = 0;
                while (retryCount < MAX_RETRIES) {
                    try {
                        SendEmailResponse sendEmailResponse = sesClient.sendEmail(sendEmailRequest);
                        log.info("sendEmail res={}" , sendEmailResponse);
                        break;
                    } catch (SdkException e) {
                        if (!(e.getCause() instanceof ServiceUnavailableException)) {
                            throw e;
                        }
                        log.info("Retrying due to service unavailable", e);
                        retryCount++;
                    }
                }
                return true;
            } catch (SdkException e) {
                handleSendEmailException(e, toAddresses);
                close();
                return false;
            }
        });
    }

    /**
     * 检查是否有剩余发送配额
     * 接近最大配额又不超过
     * @return
     */
    public boolean checkSendQuota() {
     	GetSendQuotaResponse quotaResponse = sesClient.getSendQuota();
        // 打印配额信息
        log.info("Max 24-hour send rate: " + quotaResponse.max24HourSend());
        log.info("Max send rate: " + quotaResponse.maxSendRate());
        log.info("Sent last hour: " + quotaResponse.sentLast24Hours());
        // 计算剩余配额(这里只计算了24小时内的剩余配额)
        double remainingQuota24Hours = quotaResponse.max24HourSend() - quotaResponse.sentLast24Hours();
        log.info("Remaining quota for 24 hours: " + remainingQuota24Hours);
        //发送量接近当前的配额不再使用,当前配置剩余3不再使用
        if (remainingQuota24Hours <= 3) {
            return false;
        }
        return true;
    }

    private SendEmailRequest createSendEmailRequest(List<String> toAddresses, String subject, String bodyHtml, String bodyText) {
        return SendEmailRequest.builder()
                .destination(Destination.builder().toAddresses(toAddresses).build())
                .message(Message.builder()
                        .body(Body.builder()
                                .html(Content.builder().data(bodyHtml).build())
                                .text(bodyText != null ? Content.builder().data(bodyText).build() : null)
                                .build())
                        .subject(Content.builder().data(subject).build())
                        .build())
                .source(FROM_ADDRESS)
                .configurationSetName(configurationSetName)
                .build();
    }

    /**
     * 处理发送邮件异常
     *
     * @param e           异常对象
     * @param toAddresses
     */
    private void handleSendEmailException(SdkException e, List<String> toAddresses) {
        // 这里可以添加自定义的日志记录或异常处理逻辑
        log.info("handleSendEmailException error,error=",e);
        // 其他业务处理
    }

    /**
     * 关闭SesClient资源
     */
    public void close() {
        try {
            sesClient.close();
        } catch (Exception e) {
            log.error("Failed to close SES client", e);
        }
    }

    // Example usage:
    public static void main(String[] args) {
        EmailSender sender = new EmailSender();
        try {
            // 示例:发送邮件
            List<String> toAddresses = List.of("xx@xx.com","xx@xx.com");
            String subject = "Test Email Subject";
            String bodyHtml = "<h1>Hello, this is a test email!</h1>";
            String bodyText = "Hello, this is a test email!";
            SendEmailResponse response = sender.sendEmail(toAddresses, subject, bodyHtml, bodyText);
            System.out.println(response);
            log.info("res="+ response.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

请确保将示例代码中的占位符替换为您的实际值。这段代码创建了一个SesClient实例,构建了电子邮件的各个部分(发件人、收件人、主题和正文),然后发送了电子邮件。如果发送成功,它将打印出邮件的ID;否则,将打印异常堆栈跟踪。

五、处理异常和错误

在发送电子邮件时,可能会遇到各种异常和错误,如网络问题、凭证无效或收件人地址不存在等。为了处理这些情况,您应该在代码中添加适当的异常处理逻辑。使用try-catch块捕获SesClient抛出的异常,并根据需要进行处理。您还可以考虑使用日志记录库(如Log4j或SLF4J)来记录错误信息,以便稍后进行分析和调试。

六、优化和扩展

一旦您成功集成了AWS SES并发送了电子邮件,就可以考虑如何优化和扩展您的实现。以下是一些建议:

  1. 批量发送:如果您需要发送大量电子邮件,可以考虑使用AWS SES的批量发送功能来提高效率。通过构建包含多个收件人的单个请求,您可以一次性发送多封电子邮件。
  2. 使用模板:AWS SES支持使用模板来创建具有一致外观和格式的电子邮件。您可以创建模板并在发送电子邮件时引用它们,从而简化代码并提高可维护性。
  3. 监控和日志记录:为了跟踪电子邮件的发送情况并识别潜在问题,请确保实施适当的监控和日志记录策略。您可以利用AWS CloudWatch等工具来监控SES的发送统计信息和错误指标。
  4. 安全性最佳实践:始终遵循安全性最佳实践,如使用强凭证、定期轮换密钥以及限制对SES API的访问权限。此外,确保您的应用程序能够处理敏感数据并遵守相关法规要求。

结语

通过本篇博客,您已经了解了如何在Java应用程序中集成AWS SES SDK v2来发送电子邮件。从准备开发环境到编写代码、处理异常和错误以及优化和扩展实现,我们逐步引导您完成了整个过程。现在,您可以将这些知识应用于实际项目中,并利用AWS SES的强大功能来发送事务性、营销性或批量电子邮件了。祝您编码愉快!

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AWS SES (Amazon Simple Email Service) 是亚马逊提供的可靠、灵活且可扩展的电子邮件发送和接收服务。它可以帮助开发者快速、高效地通过网络应用程序发送电子邮件。 在使用 PHP 进行 AWS SES集成时,你需要遵循以下步骤: 1. 配置 IAM 用户:首先,你需要在 AWS 管理控制台上创建一个 IAM 用户,并授予该用户适当的 SES 发送和接收权限。 2. 安装 AWS SDK for PHP:你需要在 PHP 项目安装 AWS SDK for PHP,这是一个用于与 AWS 服务进行交互的库。你可以使用 Composer 进行安装,或者手动下载并在项目引入。 3. 配置 AWS SES:在代码,你需要指定 AWS SES 的凭证、区域和其他配置信息。你可以使用 IAM 用户的凭证来进行身份验证,并设置合适的区域来确保与所需的 SES 区域进行通信。 4. 发送邮件:使用 AWS SES,你可以使用 PHP 代码来发送电子邮件。通过构建合适的电子邮件消息并指定接收者、发件人、主题和正文等信息,你可以使用 `sendEmail()` 或 `sendRawEmail()` 方法来发送邮件AWS SES 还提供其他功能,如验证发件人邮箱、配置反垃圾邮件策略、设置电子邮件模板等等。通过使用 PHP 和 AWS SES 集成,你可以方便地在你的应用程序实现强大的电子邮件功能。 总的来说,AWS SES 提供了一个强大的平台来发送和接收电子邮件,而PHP与AWS SES集成,可以让你更轻松地使用PHP发送电子邮件。这对于构建包括用户注册、密码重置、订单确认等功能的网站或应用程序来说,是一个非常有用的工具。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值