電子メールメッセージを作成する場合、HTMLとTEXTを送信するとき、またはTEXTと添付ファイルを送信するときに、Content-Typeをmultipart/alternative
に設定することになっていmultipart/mixed
ます。
では、HTML、テキスト、添付ファイルを送信したい場合はどうしますか?両方を使う?
電子メールメッセージを作成する場合、HTMLとTEXTを送信するとき、またはTEXTと添付ファイルを送信するときに、Content-Typeをmultipart/alternative
に設定することになっていmultipart/mixed
ます。
では、HTML、テキスト、添付ファイルを送信したい場合はどうしますか?両方を使う?
回答:
今日私はこの課題に直面しましたが、これらの回答は役に立ちましたが、私にとってはあまり明確ではありませんでした。
編集:これをうまくまとめたApache Commons Emailを見つけました、つまり、以下で知る必要はありません。
要件が次の電子メールの場合:
Gmail、Outlook、iPadで機能する唯一の構造は次のとおりです。
そしてコードは:
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by StrongMan on 25/05/14.
*/
public class MailContentBuilder {
private static final Pattern COMPILED_PATTERN_SRC_URL_SINGLE = Pattern.compile("src='([^']*)'", Pattern.CASE_INSENSITIVE);
private static final Pattern COMPILED_PATTERN_SRC_URL_DOUBLE = Pattern.compile("src=\"([^\"]*)\"", Pattern.CASE_INSENSITIVE);
/**
* Build an email message.
*
* The HTML may reference the embedded image (messageHtmlInline) using the filename. Any path portion is ignored to make my life easier
* e.g. If you pass in the image C:\Temp\dog.jpg you can use <img src="dog.jpg"/> or <img src="C:\Temp\dog.jpg"/> and both will work
*
* @param messageText
* @param messageHtml
* @param messageHtmlInline
* @param attachments
* @return
* @throws MessagingException
*/
public Multipart build(String messageText, String messageHtml, List<URL> messageHtmlInline, List<URL> attachments) throws MessagingException {
final Multipart mpMixed = new MimeMultipart("mixed");
{
// alternative
final Multipart mpMixedAlternative = newChild(mpMixed, "alternative");
{
// Note: MUST RENDER HTML LAST otherwise iPad mail client only renders the last image and no email
addTextVersion(mpMixedAlternative,messageText);
addHtmlVersion(mpMixedAlternative,messageHtml, messageHtmlInline);
}
// attachments
addAttachments(mpMixed,attachments);
}
//msg.setText(message, "utf-8");
//msg.setContent(message,"text/html; charset=utf-8");
return mpMixed;
}
private Multipart newChild(Multipart parent, String alternative) throws MessagingException {
MimeMultipart child = new MimeMultipart(alternative);
final MimeBodyPart mbp = new MimeBodyPart();
parent.addBodyPart(mbp);
mbp.setContent(child);
return child;
}
private void addTextVersion(Multipart mpRelatedAlternative, String messageText) throws MessagingException {
final MimeBodyPart textPart = new MimeBodyPart();
textPart.setContent(messageText, "text/plain");
mpRelatedAlternative.addBodyPart(textPart);
}
private void addHtmlVersion(Multipart parent, String messageHtml, List<URL> embeded) throws MessagingException {
// HTML version
final Multipart mpRelated = newChild(parent,"related");
// Html
final MimeBodyPart htmlPart = new MimeBodyPart();
HashMap<String,String> cids = new HashMap<String, String>();
htmlPart.setContent(replaceUrlWithCids(messageHtml,cids), "text/html");
mpRelated.addBodyPart(htmlPart);
// Inline images
addImagesInline(mpRelated, embeded, cids);
}
private void addImagesInline(Multipart parent, List<URL> embeded, HashMap<String,String> cids) throws MessagingException {
if (embeded != null)
{
for (URL img : embeded)
{
final MimeBodyPart htmlPartImg = new MimeBodyPart();
DataSource htmlPartImgDs = new URLDataSource(img);
htmlPartImg.setDataHandler(new DataHandler(htmlPartImgDs));
String fileName = img.getFile();
fileName = getFileName(fileName);
String newFileName = cids.get(fileName);
boolean imageNotReferencedInHtml = newFileName == null;
if (imageNotReferencedInHtml) continue;
// Gmail requires the cid have <> around it
htmlPartImg.setHeader("Content-ID", "<"+newFileName+">");
htmlPartImg.setDisposition(BodyPart.INLINE);
parent.addBodyPart(htmlPartImg);
}
}
}
private void addAttachments(Multipart parent, List<URL> attachments) throws MessagingException {
if (attachments != null)
{
for (URL attachment : attachments)
{
final MimeBodyPart mbpAttachment = new MimeBodyPart();
DataSource htmlPartImgDs = new URLDataSource(attachment);
mbpAttachment.setDataHandler(new DataHandler(htmlPartImgDs));
String fileName = attachment.getFile();
fileName = getFileName(fileName);
mbpAttachment.setDisposition(BodyPart.ATTACHMENT);
mbpAttachment.setFileName(fileName);
parent.addBodyPart(mbpAttachment);
}
}
}
public String replaceUrlWithCids(String html, HashMap<String,String> cids)
{
html = replaceUrlWithCids(html, COMPILED_PATTERN_SRC_URL_SINGLE, "src='cid:@cid'", cids);
html = replaceUrlWithCids(html, COMPILED_PATTERN_SRC_URL_DOUBLE, "src=\"cid:@cid\"", cids);
return html;
}
private String replaceUrlWithCids(String html, Pattern pattern, String replacement, HashMap<String,String> cids) {
Matcher matcherCssUrl = pattern.matcher(html);
StringBuffer sb = new StringBuffer();
while (matcherCssUrl.find())
{
String fileName = matcherCssUrl.group(1);
// Disregarding file path, so don't clash your filenames!
fileName = getFileName(fileName);
// A cid must start with @ and be globally unique
String cid = "@" + UUID.randomUUID().toString() + "_" + fileName;
if (cids.containsKey(fileName))
cid = cids.get(fileName);
else
cids.put(fileName,cid);
matcherCssUrl.appendReplacement(sb,replacement.replace("@cid",cid));
}
matcherCssUrl.appendTail(sb);
html = sb.toString();
return html;
}
private String getFileName(String fileName) {
if (fileName.contains("/"))
fileName = fileName.substring(fileName.lastIndexOf("/")+1);
return fileName;
}
}
そして、Gmailからの使用例
/**
* Created by StrongMan on 25/05/14.
*/
import com.sun.mail.smtp.SMTPTransport;
import java.net.URL;
import java.security.Security;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.URLDataSource;
import javax.mail.*;
import javax.mail.internet.*;
/**
*
* http://stackoverflow.com/questions/14744197/best-practices-sending-javamail-mime-multipart-emails-and-gmail
* http://stackoverflow.com/questions/3902455/smtp-multipart-alternative-vs-multipart-mixed
*
*
*
* @author doraemon
*/
public class GoogleMail {
private GoogleMail() {
}
/**
* Send email using GMail SMTP server.
*
* @param username GMail username
* @param password GMail password
* @param recipientEmail TO recipient
* @param title title of the message
* @param messageText message to be sent
* @throws AddressException if the email address parse failed
* @throws MessagingException if the connection is dead or not in the connected state or if the message is not a MimeMessage
*/
public static void Send(final String username, final String password, String recipientEmail, String title, String messageText, String messageHtml, List<URL> messageHtmlInline, List<URL> attachments) throws AddressException, MessagingException {
GoogleMail.Send(username, password, recipientEmail, "", title, messageText, messageHtml, messageHtmlInline,attachments);
}
/**
* Send email using GMail SMTP server.
*
* @param username GMail username
* @param password GMail password
* @param recipientEmail TO recipient
* @param ccEmail CC recipient. Can be empty if there is no CC recipient
* @param title title of the message
* @param messageText message to be sent
* @throws AddressException if the email address parse failed
* @throws MessagingException if the connection is dead or not in the connected state or if the message is not a MimeMessage
*/
public static void Send(final String username, final String password, String recipientEmail, String ccEmail, String title, String messageText, String messageHtml, List<URL> messageHtmlInline, List<URL> attachments) throws AddressException, MessagingException {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
// Get a Properties object
Properties props = System.getProperties();
props.setProperty("mail.smtps.host", "smtp.gmail.com");
props.setProperty("mail.smtp.socketFactory.class", SSL_FACTORY);
props.setProperty("mail.smtp.socketFactory.fallback", "false");
props.setProperty("mail.smtp.port", "465");
props.setProperty("mail.smtp.socketFactory.port", "465");
props.setProperty("mail.smtps.auth", "true");
/*
If set to false, the QUIT command is sent and the connection is immediately closed. If set
to true (the default), causes the transport to wait for the response to the QUIT command.
ref : http://java.sun.com/products/javamail/javadocs/com/sun/mail/smtp/package-summary.html
http://forum.java.sun.com/thread.jspa?threadID=5205249
smtpsend.java - demo program from javamail
*/
props.put("mail.smtps.quitwait", "false");
Session session = Session.getInstance(props, null);
// -- Create a new message --
final MimeMessage msg = new MimeMessage(session);
// -- Set the FROM and TO fields --
msg.setFrom(new InternetAddress(username + "@gmail.com"));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipientEmail, false));
if (ccEmail.length() > 0) {
msg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(ccEmail, false));
}
msg.setSubject(title);
// mixed
MailContentBuilder mailContentBuilder = new MailContentBuilder();
final Multipart mpMixed = mailContentBuilder.build(messageText, messageHtml, messageHtmlInline, attachments);
msg.setContent(mpMixed);
msg.setSentDate(new Date());
SMTPTransport t = (SMTPTransport)session.getTransport("smtps");
t.connect("smtp.gmail.com", username, password);
t.sendMessage(msg, msg.getAllRecipients());
t.close();
}
}
"<" id-left "@" id-right ">"
。
アタッチメントmultipart/mixed
の最初のパーツmultipart/alternative
および後続のパーツと一緒に使用します。次に、パーツ内のパーツtext/plain
とtext/html
パーツを使用しますmultipart/alternative
。
次に、対応する電子メールクライアントがmultipart/alternative
パーツを認識し、必要に応じてテキストパーツまたはHTMLパーツを表示します。また、後続のすべてのパーツがアタッチメントパーツとして表示されます。
ここで注意すべき重要な点は、マルチパートMIMEメッセージでは、パート内にパートがあることは完全に有効であることです。理論的には、その入れ子はどの深さにも拡張できます。合理的に機能する電子メールクライアントはすべてのメッセージパーツを再帰的に処理できるはずです。
multipart/related
はどうですか、いつこれを使用するのですか?
multipart/alternative
付属部品の一方のみが表示されなければならない意味-例えば、1つの部分があるtext/plain
と一部ですtext/html
。したがって、電子メールクライアントは両方の部分を表示するのではなく、1つだけを表示する必要があります。すなわち、それらは関連していません。multipart/related
は、さまざまなサブパーツがすべてメインルートパーツの一部であることを示します。たとえば、メインパーツはtext/html
で、サブパーツは埋め込み画像です。詳細はこちらをご覧ください。
mutt
CLIクライアントを使用して、マルチパートMIMEメッセージが正しく設定されていることを確認できます。v
メッセージの表示中にを押すと、メッセージが表示され、MIMEパーツのネストされたツリーをたどることができます。
メッセージにはコンテンツがあります。コンテンツは、テキスト、html、DataHandler、またはマルチパートにすることができ、コンテンツは1つだけです。マルチパートにはBodyPartしかありませんが、複数持つことができます。メッセージのようなBodyPartは、すでに説明されているコンテンツを持つことができます。
HTML、テキスト、添付ファイルを含むメッセージは、次のように階層的に表示できます。
message
mainMultipart (content for message, subType="mixed")
->htmlAndTextBodyPart (bodyPart1 for mainMultipart)
->htmlAndTextMultipart (content for htmlAndTextBodyPart, subType="alternative")
->textBodyPart (bodyPart2 for the htmlAndTextMultipart)
->text (content for textBodyPart)
->htmlBodyPart (bodyPart1 for htmlAndTextMultipart)
->html (content for htmlBodyPart)
->fileBodyPart1 (bodyPart2 for the mainMultipart)
->FileDataHandler (content for fileBodyPart1 )
そして、そのようなメッセージを作成するコード:
// the parent or main part if you will
Multipart mainMultipart = new MimeMultipart("mixed");
// this will hold text and html and tells the client there are 2 versions of the message (html and text). presumably text
// being the alternative to html
Multipart htmlAndTextMultipart = new MimeMultipart("alternative");
// set text
MimeBodyPart textBodyPart = new MimeBodyPart();
textBodyPart.setText(text);
htmlAndTextMultipart.addBodyPart(textBodyPart);
// set html (set this last per rfc1341 which states last = best)
MimeBodyPart htmlBodyPart = new MimeBodyPart();
htmlBodyPart.setContent(html, "text/html; charset=utf-8");
htmlAndTextMultipart.addBodyPart(htmlBodyPart);
// stuff the multipart into a bodypart and add the bodyPart to the mainMultipart
MimeBodyPart htmlAndTextBodyPart = new MimeBodyPart();
htmlAndTextBodyPart.setContent(htmlAndTextMultipart);
mainMultipart.addBodyPart(htmlAndTextBodyPart);
// attach file body parts directly to the mainMultipart
MimeBodyPart filePart = new MimeBodyPart();
FileDataSource fds = new FileDataSource("/path/to/some/file.txt");
filePart.setDataHandler(new DataHandler(fds));
filePart.setFileName(fds.getName());
mainMultipart.addBodyPart(filePart);
// set message content
message.setContent(mainMultipart);
私はこの問題にぶつかった。このアーキテクチャ(レインの回答から)は私にとってはうまくいきました。これがPythonでの解決策です。
主なメール作成機能は次のとおりです。
def create_message_with_attachment(
sender, to, subject, msgHtml, msgPlain, attachmentFile):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
file: The path to the file to be attached.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart('mixed')
message['to'] = to
message['from'] = sender
message['subject'] = subject
message_alternative = MIMEMultipart('alternative')
message_related = MIMEMultipart('related')
message_related.attach(MIMEText(msgHtml, 'html'))
message_alternative.attach(MIMEText(msgPlain, 'plain'))
message_alternative.attach(message_related)
message.attach(message_alternative)
print "create_message_with_attachment: file:", attachmentFile
content_type, encoding = mimetypes.guess_type(attachmentFile)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(attachmentFile, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(attachmentFile, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(attachmentFile, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(attachmentFile, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
filename = os.path.basename(attachmentFile)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
次に、html / text / attachmentを含むメールを送信するための完全なコードを示します。
import httplib2
import os
import oauth2client
from oauth2client import client, tools
import base64
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from apiclient import errors, discovery
import mimetypes
from email.mime.image import MIMEImage
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
SCOPES = 'https://www.googleapis.com/auth/gmail.send'
CLIENT_SECRET_FILE1 = 'client_secret.json'
location = os.path.realpath(
os.path.join(os.getcwd(), os.path.dirname(__file__)))
CLIENT_SECRET_FILE = os.path.join(location, CLIENT_SECRET_FILE1)
APPLICATION_NAME = 'Gmail API Python Send Email'
def get_credentials():
home_dir = os.path.expanduser('~')
credential_dir = os.path.join(home_dir, '.credentials')
if not os.path.exists(credential_dir):
os.makedirs(credential_dir)
credential_path = os.path.join(credential_dir,
'gmail-python-email-send.json')
store = oauth2client.file.Storage(credential_path)
credentials = store.get()
if not credentials or credentials.invalid:
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
flow.user_agent = APPLICATION_NAME
credentials = tools.run_flow(flow, store)
print 'Storing credentials to ' + credential_path
return credentials
def SendMessageWithAttachment(sender, to, subject, msgHtml, msgPlain, attachmentFile):
credentials = get_credentials()
http = credentials.authorize(httplib2.Http())
service = discovery.build('gmail', 'v1', http=http)
message1 = create_message_with_attachment(sender, to, subject, msgHtml, msgPlain, attachmentFile)
SendMessageInternal(service, "me", message1)
def SendMessageInternal(service, user_id, message):
try:
message = (service.users().messages().send(userId=user_id, body=message).execute())
print 'Message Id: %s' % message['id']
return message
except errors.HttpError, error:
print 'An error occurred: %s' % error
return "error"
def create_message_with_attachment(
sender, to, subject, msgHtml, msgPlain, attachmentFile):
"""Create a message for an email.
Args:
sender: Email address of the sender.
to: Email address of the receiver.
subject: The subject of the email message.
message_text: The text of the email message.
file: The path to the file to be attached.
Returns:
An object containing a base64url encoded email object.
"""
message = MIMEMultipart('mixed')
message['to'] = to
message['from'] = sender
message['subject'] = subject
message_alternative = MIMEMultipart('alternative')
message_related = MIMEMultipart('related')
message_related.attach(MIMEText(msgHtml, 'html'))
message_alternative.attach(MIMEText(msgPlain, 'plain'))
message_alternative.attach(message_related)
message.attach(message_alternative)
print "create_message_with_attachment: file:", attachmentFile
content_type, encoding = mimetypes.guess_type(attachmentFile)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
if main_type == 'text':
fp = open(attachmentFile, 'rb')
msg = MIMEText(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'image':
fp = open(attachmentFile, 'rb')
msg = MIMEImage(fp.read(), _subtype=sub_type)
fp.close()
elif main_type == 'audio':
fp = open(attachmentFile, 'rb')
msg = MIMEAudio(fp.read(), _subtype=sub_type)
fp.close()
else:
fp = open(attachmentFile, 'rb')
msg = MIMEBase(main_type, sub_type)
msg.set_payload(fp.read())
fp.close()
filename = os.path.basename(attachmentFile)
msg.add_header('Content-Disposition', 'attachment', filename=filename)
message.attach(msg)
return {'raw': base64.urlsafe_b64encode(message.as_string())}
def main():
to = "to@address.com"
sender = "from@address.com"
subject = "subject"
msgHtml = "Hi<br/>Html Email"
msgPlain = "Hi\nPlain Email"
attachment = "/path/to/file.pdf"
SendMessageWithAttachment(sender, to, subject, msgHtml, msgPlain, attachment)
if __name__ == '__main__':
main()
Iainの例を基にして、これらの電子メールを個別のプレーンテキスト、HTML、および複数の添付ファイルで作成する同様のニーズがありましたが、PHPを使用しました。Amazon SESを使用して添付ファイル付きのEメールを送信しているので、APIでは現在、sendRawEmail(...)関数を使用してEメールを最初から作成する必要があります。
多くの調査の結果(通常のフラストレーションよりも大きい)、問題は解決され、PHPソースコードが投稿されたため、同様の問題が発生している他の人を助けることができます。これが誰かを助けてくれることを願っています-私がこの問題に取り組むことを強制されたサルの軍隊は今疲れ果てています。
Amazon SESを使用して添付ファイル付きのEメールを送信するためのPHPソースコード。
<?php
require_once('AWSSDKforPHP/aws.phar');
use Aws\Ses\SesClient;
/**
* SESUtils is a tool to make it easier to work with Amazon Simple Email Service
* Features:
* A client to prepare emails for use with sending attachments or not
*
* There is no warranty - use this code at your own risk.
* @author sbossen with assistance from Michael Deal
* http://righthandedmonkey.com
*
* Update: Error checking and new params input array provided by Michael Deal
* Update2: Corrected for allowing to send multiple attachments and plain text/html body
* Ref: Http://stackoverflow.com/questions/3902455/smtp-multipart-alternative-vs-multipart-mixed/
*/
class SESUtils {
const version = "1.0";
const AWS_KEY = "YOUR-KEY";
const AWS_SEC = "YOUR-SECRET";
const AWS_REGION = "us-east-1";
const MAX_ATTACHMENT_NAME_LEN = 60;
/**
* Usage:
$params = array(
"to" => "email1@gmail.com",
"subject" => "Some subject",
"message" => "<strong>Some email body</strong>",
"from" => "sender@verifiedbyaws",
//OPTIONAL
"replyTo" => "reply_to@gmail.com",
//OPTIONAL
"files" => array(
1 => array(
"name" => "filename1",
"filepath" => "/path/to/file1.txt",
"mime" => "application/octet-stream"
),
2 => array(
"name" => "filename2",
"filepath" => "/path/to/file2.txt",
"mime" => "application/octet-stream"
),
)
);
$res = SESUtils::sendMail($params);
* NOTE: When sending a single file, omit the key (ie. the '1 =>')
* or use 0 => array(...) - otherwise the file will come out garbled
* ie. use:
* "files" => array(
* 0 => array( "name" => "filename", "filepath" => "path/to/file.txt",
* "mime" => "application/octet-stream")
*
* For the 'to' parameter, you can send multiple recipiants with an array
* "to" => array("email1@gmail.com", "other@msn.com")
* use $res->success to check if it was successful
* use $res->message_id to check later with Amazon for further processing
* use $res->result_text to look for error text if the task was not successful
*
* @param array $params - array of parameters for the email
* @return \ResultHelper
*/
public static function sendMail($params) {
$to = self::getParam($params, 'to', true);
$subject = self::getParam($params, 'subject', true);
$body = self::getParam($params, 'message', true);
$from = self::getParam($params, 'from', true);
$replyTo = self::getParam($params, 'replyTo');
$files = self::getParam($params, 'files');
$res = new ResultHelper();
// get the client ready
$client = SesClient::factory(array(
'key' => self::AWS_KEY,
'secret' => self::AWS_SEC,
'region' => self::AWS_REGION
));
// build the message
if (is_array($to)) {
$to_str = rtrim(implode(',', $to), ',');
} else {
$to_str = $to;
}
$msg = "To: $to_str\n";
$msg .= "From: $from\n";
if ($replyTo) {
$msg .= "Reply-To: $replyTo\n";
}
// in case you have funny characters in the subject
$subject = mb_encode_mimeheader($subject, 'UTF-8');
$msg .= "Subject: $subject\n";
$msg .= "MIME-Version: 1.0\n";
$msg .= "Content-Type: multipart/mixed;\n";
$boundary = uniqid("_Part_".time(), true); //random unique string
$boundary2 = uniqid("_Part2_".time(), true); //random unique string
$msg .= " boundary=\"$boundary\"\n";
$msg .= "\n";
// now the actual body
$msg .= "--$boundary\n";
//since we are sending text and html emails with multiple attachments
//we must use a combination of mixed and alternative boundaries
//hence the use of boundary and boundary2
$msg .= "Content-Type: multipart/alternative;\n";
$msg .= " boundary=\"$boundary2\"\n";
$msg .= "\n";
$msg .= "--$boundary2\n";
// first, the plain text
$msg .= "Content-Type: text/plain; charset=utf-8\n";
$msg .= "Content-Transfer-Encoding: 7bit\n";
$msg .= "\n";
$msg .= strip_tags($body); //remove any HTML tags
$msg .= "\n";
// now, the html text
$msg .= "--$boundary2\n";
$msg .= "Content-Type: text/html; charset=utf-8\n";
$msg .= "Content-Transfer-Encoding: 7bit\n";
$msg .= "\n";
$msg .= $body;
$msg .= "\n";
$msg .= "--$boundary2--\n";
// add attachments
if (is_array($files)) {
$count = count($files);
foreach ($files as $file) {
$msg .= "\n";
$msg .= "--$boundary\n";
$msg .= "Content-Transfer-Encoding: base64\n";
$clean_filename = self::clean_filename($file["name"], self::MAX_ATTACHMENT_NAME_LEN);
$msg .= "Content-Type: {$file['mime']}; name=$clean_filename;\n";
$msg .= "Content-Disposition: attachment; filename=$clean_filename;\n";
$msg .= "\n";
$msg .= base64_encode(file_get_contents($file['filepath']));
$msg .= "\n--$boundary";
}
// close email
$msg .= "--\n";
}
// now send the email out
try {
$ses_result = $client->sendRawEmail(
array(
'RawMessage' => array(
'Data' => base64_encode($msg)
)
), array(
'Source' => $from,
'Destinations' => $to_str
)
);
if ($ses_result) {
$res->message_id = $ses_result->get('MessageId');
} else {
$res->success = false;
$res->result_text = "Amazon SES did not return a MessageId";
}
} catch (Exception $e) {
$res->success = false;
$res->result_text = $e->getMessage().
" - To: $to_str, Sender: $from, Subject: $subject";
}
return $res;
}
private static function getParam($params, $param, $required = false) {
$value = isset($params[$param]) ? $params[$param] : null;
if ($required && empty($value)) {
throw new Exception('"'.$param.'" parameter is required.');
} else {
return $value;
}
}
/**
Clean filename function - to get a file friendly
**/
public static function clean_filename($str, $limit = 0, $replace=array(), $delimiter='-') {
if( !empty($replace) ) {
$str = str_replace((array)$replace, ' ', $str);
}
$clean = iconv('UTF-8', 'ASCII//TRANSLIT', $str);
$clean = preg_replace("/[^a-zA-Z0-9\.\/_| -]/", '', $clean);
$clean = preg_replace("/[\/| -]+/", '-', $clean);
if ($limit > 0) {
//don't truncate file extension
$arr = explode(".", $clean);
$size = count($arr);
$base = "";
$ext = "";
if ($size > 0) {
for ($i = 0; $i < $size; $i++) {
if ($i < $size - 1) { //if it's not the last item, add to $bn
$base .= $arr[$i];
//if next one isn't last, add a dot
if ($i < $size - 2)
$base .= ".";
} else {
if ($i > 0)
$ext = ".";
$ext .= $arr[$i];
}
}
}
$bn_size = mb_strlen($base);
$ex_size = mb_strlen($ext);
$bn_new = mb_substr($base, 0, $limit - $ex_size);
// doing again in case extension is long
$clean = mb_substr($bn_new.$ext, 0, $limit);
}
return $clean;
}
}
class ResultHelper {
public $success = true;
public $result_text = "";
public $message_id = "";
}
?>
$boundary
、添付ファイル付きの本文全体が$boundary2
含まれますが、HTMLまたはプレーンテキストのみが含まれます。優れたソリューション。教えてください、これはプレーンテキストを送信するためのソリューションですが、メールクライアントがHTMLをサポートしていない場合のこの代替メッセージですか?ありがとう!
グレートアンサーレイン!
幅広いデバイスでこれを機能させるためにいくつかのことを行いました。最後に、テストしたクライアントをリストします。
パラメーターアタッチメントを含まず、MimeMultipart( "mixed")を使用しない新しいビルドコンストラクターを追加しました。インライン画像のみを送信する場合は、混合する必要はありません。
public Multipart build(String messageText, String messageHtml, List<URL> messageHtmlInline) throws MessagingException {
final Multipart mpAlternative = new MimeMultipart("alternative");
{
// Note: MUST RENDER HTML LAST otherwise iPad mail client only renders
// the last image and no email
addTextVersion(mpAlternative,messageText);
addHtmlVersion(mpAlternative,messageHtml, messageHtmlInline);
}
return mpAlternative;
}
addTextVersionメソッドでは、コンテンツを追加するときに文字セットを追加しましたが、これはおそらく渡すことができる/すべきですが、静的に追加しただけです。
textPart.setContent(messageText, "text/plain");
to
textPart.setContent(messageText, "text/plain; charset=UTF-8");
最後のアイテムはaddImagesInlineメソッドに追加されていました。次のコードでヘッダーに画像ファイル名の設定を追加しました。これを行わないと、少なくともAndroidのデフォルトのメールクライアントでは、不明な名前のインライン画像が含まれ、自動的にダウンロードされてメールに表示されません。
for (URL img : embeded) {
final MimeBodyPart htmlPartImg = new MimeBodyPart();
DataSource htmlPartImgDs = new URLDataSource(img);
htmlPartImg.setDataHandler(new DataHandler(htmlPartImgDs));
String fileName = img.getFile();
fileName = getFileName(fileName);
String newFileName = cids.get(fileName);
boolean imageNotReferencedInHtml = newFileName == null;
if (imageNotReferencedInHtml) continue;
htmlPartImg.setHeader("Content-ID", "<"+newFileName+">");
htmlPartImg.setDisposition(BodyPart.INLINE);
**htmlPartImg.setFileName(newFileName);**
parent.addBodyPart(htmlPartImg);
}
最後に、これは私がテストしたクライアントのリストです。Outlook 2010、Outlook Web App、Internet Explorer 11、Firefox、Chrome、Appleのネイティブアプリを使用したOutlook、Gmail経由のメール-ブラウザーメールクライアント、Internet Explorer 11、Firefox、Chrome、Androidのデフォルトメールクライアント、osx IPhoneのデフォルトメールクライアント、Gmail Android上のメールクライアント、iPhone上のGmailメールクライアント、Yahoo経由のメール-ブラウザメールクライアント、Internet Explorer 11、Firefox、Chrome、Androidのデフォルトメールクライアント、osx IPhoneのデフォルトメールクライアント。
それが他の人を助けることを願っています。
これが最高です: 添付ファイルとインライン画像を含むマルチパート/混合MIMEメッセージ
そして画像:https : //www.qcode.co.uk/images/mime-nesting-structure.png
From: from@qcode.co.uk
To: to@@qcode.co.uk
Subject: Example Email
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="MixedBoundaryString"
--MixedBoundaryString
Content-Type: multipart/related; boundary="RelatedBoundaryString"
--RelatedBoundaryString
Content-Type: multipart/alternative; boundary="AlternativeBoundaryString"
--AlternativeBoundaryString
Content-Type: text/plain;charset="utf-8"
Content-Transfer-Encoding: quoted-printable
This is the plain text part of the email.
--AlternativeBoundaryString
Content-Type: text/html;charset="utf-8"
Content-Transfer-Encoding: quoted-printable
<html>
<body>=0D
<img src=3D=22cid:masthead.png=40qcode.co.uk=22 width 800 height=3D80=
=5C>=0D
<p>This is the html part of the email.</p>=0D
<img src=3D=22cid:logo.png=40qcode.co.uk=22 width 200 height=3D60 =5C=
>=0D
</body>=0D
</html>=0D
--AlternativeBoundaryString--
--RelatedBoundaryString
Content-Type: image/jpgeg;name="logo.png"
Content-Transfer-Encoding: base64
Content-Disposition: inline;filename="logo.png"
Content-ID: <logo.png@qcode.co.uk>
amtsb2hiaXVvbHJueXZzNXQ2XHVmdGd5d2VoYmFmaGpremxidTh2b2hydHVqd255aHVpbnRyZnhu
dWkgb2l1b3NydGhpdXRvZ2hqdWlyb2h5dWd0aXJlaHN1aWhndXNpaHhidnVqZmtkeG5qaG5iZ3Vy
...
...
a25qbW9nNXRwbF0nemVycHpvemlnc3k5aDZqcm9wdHo7amlodDhpOTA4N3U5Nnkwb2tqMm9sd3An
LGZ2cDBbZWRzcm85eWo1Zmtsc2xrZ3g=
--RelatedBoundaryString
Content-Type: image/jpgeg;name="masthead.png"
Content-Transfer-Encoding: base64
Content-Disposition: inline;filename="masthead.png"
Content-ID: <masthead.png@qcode.co.uk>
aXR4ZGh5Yjd1OHk3MzQ4eXFndzhpYW9wO2tibHB6c2tqOTgwNXE0aW9qYWJ6aXBqOTBpcjl2MC1t
dGlmOTA0cW05dGkwbWk0OXQwYVttaXZvcnBhXGtsbGo7emt2c2pkZnI7Z2lwb2F1amdpNTh1NDlh
...
...
eXN6dWdoeXhiNzhuZzdnaHQ3eW9zemlqb2FqZWt0cmZ1eXZnamhka3JmdDg3aXV2dWd5aGVidXdz
dhyuhehe76YTGSFGA=
--RelatedBoundaryString--
--MixedBoundaryString
Content-Type: application/pdf;name="Invoice_1.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;filename="Invoice_1.pdf"
aGZqZGtsZ3poZHVpeWZoemd2dXNoamRibngganZodWpyYWRuIHVqO0hmSjtyRVVPIEZSO05SVURF
SEx1aWhudWpoZ3h1XGh1c2loZWRma25kamlsXHpodXZpZmhkcnVsaGpnZmtsaGVqZ2xod2plZmdq
...
...
a2psajY1ZWxqanNveHV5ZXJ3NTQzYXRnZnJhZXdhcmV0eXRia2xhanNueXVpNjRvNWllc3l1c2lw
dWg4NTA0
--MixedBoundaryString
Content-Type: application/pdf;name="SpecialOffer.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;filename="SpecialOffer.pdf"
aXBvY21odWl0dnI1dWk4OXdzNHU5NTgwcDN3YTt1OTQwc3U4NTk1dTg0dTV5OGlncHE1dW4zOTgw
cS0zNHU4NTk0eWI4OTcwdjg5MHE4cHV0O3BvYTt6dWI7dWlvenZ1em9pdW51dDlvdTg5YnE4N3Z3
...
...
OTViOHk5cDV3dTh5bnB3dWZ2OHQ5dTh2cHVpO2p2Ymd1eTg5MGg3ajY4bjZ2ODl1ZGlvcjQ1amts
dfnhgjdfihn=
--MixedBoundaryString--
.
スキーママルチパート/関連/代替
Header
|From: email
|To: email
|MIME-Version: 1.0
|Content-Type: multipart/mixed; boundary="boundary1";
Message body
|multipart/mixed --boundary1
|--boundary1
| multipart/related --boundary2
| |--boundary2
| | multipart/alternative --boundary3
| | |--boundary3
| | |text/plain
| | |--boundary3
| | |text/html
| | |--boundary3--
| |--boundary2
| |Inline image
| |--boundary2
| |Inline image
| |--boundary2--
|--boundary1
|Attachment1
|--boundary1
|Attachment2
|--boundary1
|Attachment3
|--boundary1--
|
.