Saltar al contenido

Correo multiparte / alternativo vs multiparte /mixed

Hola, encontramos la respuesta a lo que buscabas, deslízate y la verás a continuación.

Entré a este desafío hoy y encontré estas respuestas útiles pero no lo suficientemente explícitas para mí.

Editar: Acabo de encontrar el correo electrónico de Apache Commons que lo resume muy bien, lo que significa que no necesita saberlo a continuación.

Si su requerimiento es un correo electrónico con:

  1. versiones de texto y html
  2. la versión html tiene imágenes incrustadas (en línea)
  3. archivos adjuntos

La única estructura que encontré que funciona con Gmail / Outlook / iPad es:

  • mixed
    • alternativa
      • texto
      • relacionado
        • html
        • imagen en línea
        • imagen en línea
    • adjunto
    • adjunto

Y el código es:

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:Tempdog.jpg you can use 
or and both will work * * @param messageText * @param messageHtml * @param messageHtmlInline * @param attachments * @return * @throws MessagingException */ public Multipart build(String messageText, String messageHtml, List messageHtmlInline, List 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 embeded) throws MessagingException // HTML version final Multipart mpRelated = newChild(parent,"related"); // Html final MimeBodyPart htmlPart = new MimeBodyPart(); HashMap cids = new HashMap(); htmlPart.setContent(replaceUrlWithCids(messageHtml,cids), "text/html"); mpRelated.addBodyPart(htmlPart); // Inline images addImagesInline(mpRelated, embeded, cids); private void addImagesInline(Multipart parent, List embeded, HashMap 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 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 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 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;

Y un ejemplo de cómo usarlo con 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 messageHtmlInline, List 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 messageHtmlInline, List 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();
    


Usar multipart/mixed con la primera parte como multipart/alternative y partes posteriores para los accesorios. A su vez, utilice text/plain y text/html partes dentro del multipart/alternative parte.

Un cliente de correo electrónico capaz debería reconocer el multipart/alternative part y muestre la parte de texto o la parte html según sea necesario. También debe mostrar todas las piezas siguientes como piezas adjuntas.

Lo importante a tener en cuenta aquí es que, en mensajes MIME de varias partes, es perfectamente válido tener partes dentro de partes. En teoría, esa anidación puede extenderse a cualquier profundidad. Cualquier cliente de correo electrónico razonablemente capaz debería poder recursivamente procesar todas las partes del mensaje.

Los mensajes tienen contenido. El contenido puede ser texto, html, DataHandler o Multipart, y solo puede haber un contenido. Las partes múltiples solo tienen partes del cuerpo, pero pueden tener más de una. BodyParts, como Messages, puede tener contenido que ya ha sido descrito.

Un mensaje con HTML, texto y un archivo adjunto se puede ver jerárquicamente de la siguiente manera:

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 )

Y el código para construir tal mensaje:

    // 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);

Valoraciones y reseñas

Eres capaz de amparar nuestra publicación mostrando un comentario y valorándolo te lo agradecemos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)


Tags :

Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *