使用Amazon SES sendRawEmail发邮件仅BCC生效,TO/CC失效求解决
我在使用Amazon SES的sendRawEmail接口发送邮件时遇到异常:只有当收件人在BCC邮件头时能成功发送,TO或CC字段包含收件人时完全发不出去。查了官方文档也没找到原因,附上我的代码,求帮忙排查:
public void send(Payload request) throws MessagingException, IOException { List<String> recipients = new LinkedList<>(); String from = request.getFrom(); String targetEmail = request.getRecipient(); String ccEmail = request.getCc(); String bccEmail = request.getBcc(); Session session = Session.getDefaultInstance(new Properties()); MimeMessage message = new MimeMessage(session); message.setSubject(request.getSubject()); message.setFrom(new InternetAddress(from)); if (targetEmail != null) { message.setRecipients(RecipientType.TO, InternetAddress.parse(targetEmail)); recipients.add(targetEmail); } if (ccEmail != null) { message.setRecipients(RecipientType.CC, InternetAddress.parse(ccEmail)); recipients.add(ccEmail); } if (bccEmail != null) { message.setRecipients(RecipientType.BCC, InternetAddress.parse(bccEmail)); recipients.add(bccEmail); } MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(request.getBody(), "text/html; charset=UTF-8;"); MimeMultipart bodyContainer = new MimeMultipart("alternative"); bodyContainer.addBodyPart(htmlPart); MimeBodyPart htmlWrapper = new MimeBodyPart(); htmlWrapper.setContent(bodyContainer); MimeMultipart messageContainer = new MimeMultipart("mixed"); messageContainer.addBodyPart(htmlWrapper); Attachment attachment = request.getAttachment(); if (Objects.nonNull(attachment)) { MimeBodyPart attachmentBodyPart = new MimeBodyPart(); DataHandler dataHandler = new DataHandler(attachment.getByteArrayDataSource()); attachmentBodyPart.setDataHandler(dataHandler); attachmentBodyPart.setFileName(attachment.getFileName()); messageContainer.addBodyPart(attachmentBodyPart); } message.setContent(messageContainer); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); message.writeTo(outputStream); RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(outputStream.toByteArray())); SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage); rawEmailRequest.withSource(from); rawEmailRequest.withDestinations(recipients); SendRawEmailResult sendRawEmailResult = amazonSimpleEmailService.sendRawEmail(rawEmailRequest); log.info("Email sent. MessageId: {}", targetEmail, ccEmail, bccEmail, sendRawEmailResult.getMessageId()); }
核心问题分析
你的代码存在两个关键问题,直接导致TO/CC收件人无法正常发送:
收件人列表格式错误
当targetEmail或ccEmail是多个用逗号分隔的邮箱地址时,InternetAddress.parse()能正确解析为多个InternetAddress对象,但你直接把原始的带逗号的字符串(比如"a@xx.com,b@xx.com")添加到recipients列表中。而SES的withDestinations()要求列表中的每个元素都是单个有效的邮箱地址,不是带分隔符的字符串。这种情况下,TO/CC对应的地址会被SES判定为无效邮箱,导致发送失败;如果你的BCC只传单个邮箱,就不会触发这个问题。SendRawEmail参数逻辑冲突
使用sendRawEmail时,如果你指定了Destinations参数,SES会忽略邮件头中的TO/CC/BCC信息,直接使用这个参数里的地址作为收件人。你同时在邮件头设置了TO/CC,又把这些地址加到Destinations,虽然逻辑上看似一致,但格式错误的Destinations列表会直接导致TO/CC的地址无法被正确识别。
修复方案
1. 正确拆分收件人到列表
不要直接添加原始字符串,而是将InternetAddress.parse()返回的地址逐个提取出来加入recipients:
// 替换原有的TO/CC/BCC处理逻辑 if (targetEmail != null) { InternetAddress[] toAddresses = InternetAddress.parse(targetEmail); message.setRecipients(RecipientType.TO, toAddresses); for (InternetAddress addr : toAddresses) { recipients.add(addr.getAddress()); } } if (ccEmail != null) { InternetAddress[] ccAddresses = InternetAddress.parse(ccEmail); message.setRecipients(RecipientType.CC, ccAddresses); for (InternetAddress addr : ccAddresses) { recipients.add(addr.getAddress()); } } if (bccEmail != null) { InternetAddress[] bccAddresses = InternetAddress.parse(bccEmail); message.setRecipients(RecipientType.BCC, bccAddresses); for (InternetAddress addr : bccAddresses) { recipients.add(addr.getAddress()); } }
2. 可选优化:移除Destinations参数(推荐)
如果你已经在原始邮件中正确设置了TO/CC/BCC头,可以直接移除withDestinations()的调用。SES会自动解析原始邮件中的收件人信息,这样能避免参数和邮件头的冲突:
SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage); rawEmailRequest.withSource(from); // 移除这一行:rawEmailRequest.withDestinations(recipients);
额外排查点
- 确认发件地址
from已经在SES控制台完成验证(邮箱或域名验证),SES不允许发送未验证的发件地址。 - 确认TO/CC的收件人地址没有被SES列入黑名单,且如果是发送到外部邮箱,你的SES账户已经移出沙箱(沙箱环境下只能发送到已验证的收件人)。
内容的提问来源于stack exchange,提问作者N. G.

