【问题标题】:Inline images in email using JavaMail使用 JavaMail 在电子邮件中嵌入图像
【发布时间】:2011-03-01 02:30:29
【问题描述】:

我想使用 javamail 发送一封内嵌图片的电子邮件。

我正在做这样的事情。

MimeMultipart content = new MimeMultipart("related");

BodyPart bodyPart = new MimeBodyPart();
bodyPart.setContent(message, "text/html; charset=ISO-8859-1");
content.addBodyPart(bodyPart);

bodyPart = new MimeBodyPart();
DataSource ds = new ByteArrayDataSource(image, "image/jpeg");
bodyPart.setDataHandler(new DataHandler(ds));
bodyPart.setHeader("Content-Type", "image/jpeg; name=image.jpg");
bodyPart.setHeader("Content-ID", "<image>");
bodyPart.setHeader("Content-Disposition", "inline");
content.addBodyPart(bodyPart);

msg.setContent(content);

我也试过

    bodyPart.setHeader("inline; filename=image.jpg");

    bodyPart.setDisposition("inline");

但无论如何,图像都是作为附件发送的,而 Content-Dispostion 正在变成“附件”。

如何使用 javamail 在电子邮件中内联发送图像?

【问题讨论】:

    标签: java mime mime-types jakarta-mail multipart


    【解决方案1】:

    你的问题

    据我所知,它看起来像您创建消息的方式,并且一切都正确!您使用正确的 MIME 类型和所有内容。

    我不确定您为什么使用 DataSource 和 DataHandler,并且在图像上有一个 ContentID,但您需要完成您的问题,以便我能够解决更多问题。特别是下面这行:

    bodyPart.setContent(message, "text/html; charset=ISO-8859-1");
    

    message 中有什么内容?是否包含&lt;img src="cid:image" /&gt;

    您是否尝试使用 String cid = ContentIdGenerator.getContentId(); 而不是使用 image 生成 ContentID


    来源

    这篇博客文章教会了我如何使用正确的消息类型、附上我的图片并从 HTML 正文中引用附件:How to Send Email with Embedded Images Using Java


    详情

    留言

    您必须使用 MimeMultipart 类创建您的内容。重要的是使用字符串"related" 作为构造函数的参数,告诉JavaMail 你的部分“一起工作”

    MimeMultipart content = new MimeMultipart("related");
    

    内容标识符

    您需要生成一个 ContentID,它是一个字符串,用于标识您附加到电子邮件的图像并从电子邮件正文中引用它。

    String cid = ContentIdGenerator.getContentId();
    

    注意:这个ContentIdGenerator 类是假设的。您可以创建一个或内联创建内容 ID。就我而言,我使用了一个简单的方法:

    import java.util.UUID;
    
    // ...
    
    String generateContentId(String prefix) {
         return String.format("%s-%s", prefix, UUID.randomUUID());
    }
    

    HTML 正文

    HTML 代码是MimeMultipart 内容的一部分。为此使用MimeBodyPart 类。设置该部分的文字时不要忘记指定encoding"html"

    MimeBodyPart htmlPart = new MimeBodyPart();
    htmlPart.setText(""
      + "<html>"
      + " <body>"
      + "  <p>Here is my image:</p>"
      + "  <img src=\"cid:" + cid + "\" />"
      + " </body>"
      + "</html>" 
      ,"US-ASCII", "html");
    content.addBodyPart(htmlPart);
    

    请注意,作为图像的来源,我们使用cid: 和生成的 ContentID。

    图片附件

    我们可以为图片的附件创建另一个MimeBodyPart

    MimeBodyPart imagePart = new MimeBodyPart();
    imagePart.attachFile("resources/teapot.jpg");
    imagePart.setContentID("<" + cid + ">");
    imagePart.setDisposition(MimeBodyPart.INLINE);
    content.addBodyPart(imagePart);
    

    请注意,我们在 &lt;&gt; 之间使用相同的 ContentID,并将其设置为图像的 ContentID。我们还将处置设置为 INLINE 以表明此图像旨在显示在电子邮件中,而不是作为附件。

    完成消息

    就是这样!如果您在正确的会话上创建 SMTP 消息并使用该内容,您的电子邮件将包含嵌入的图像!例如:

    SMTPMessage m = new SMTPMessage(session);
    m.setContent(content);
    m.setSubject("Mail with embedded image");
    m.setRecipient(RecipientType.TO, new InternetAddress("your@email.com"));
    Transport.send(m)
    

    让我知道这是否适合你! ;)

    【讨论】:

    • attachFile 不是MimeBodyPart 的方法,根据我的IDE。我找到了另一种解决方案:DataSource fds = new FileDataSource("teapot.jpg"); messageBodyPart.setDataHandler(new DataHandler(fds));.
    • 找到了,但还是不满意:attachFile is part of JavaMail >= 1.4;但是,我使用的是 1.5.3,使用单独的部分(mailapi-1.5.3.jar 和 smtp-1.5.3.jar)以及完整的 API(javax.mail-1.5.3.jar)对其进行了测试,但是attachFile 不可用。
    • 我刚刚检查过,我看到attachFileMimeBodyPart in version 1.4.7 的一种方法。我刚看了看,它似乎也在那里in version 1.5.2。我在网上找不到 1.5.3 版的资源 :(
    • 你好,你为 ContentIdGenerator 安装了什么 jar?
    • 嘿@AzaSuhaza,对不起,我最初的回答不清楚。 ContentIdGenerator 是一个假设的类。就我而言,我使用 java.util.UUID 就像这样 UUID.randomUUID()
    【解决方案2】:

    你为什么不试试这样的东西?

        MimeMessage mail =  new MimeMessage(mailSession);
    
        mail.setSubject(subject);
    
        MimeBodyPart messageBodyPart = new MimeBodyPart();
    
        messageBodyPart.setContent(message, "text/html");
    
        Multipart multipart = new MimeMultipart();
        multipart.addBodyPart(messageBodyPart);
    
        messageBodyPart = new MimeBodyPart();
        DataSource source = new FileDataSource(new File("complete path to image.jpg"));
        messageBodyPart.setDataHandler(new DataHandler(source));
        messageBodyPart.setFileName(fileAttachment.getName());
        messageBodyPart.setDisposition(MimeBodyPart.INLINE);
        multipart.addBodyPart(messageBodyPart);
    
        mail.setContent(multipart);
    

    在消息中,有一个 <img src="image.jpg"/> 标签,你应该没问题。

    祝你好运

    【讨论】:

    • 在邮件正文中包含 img 标签很重要。如果您的邮件客户端无法识别该图像在正文中使用,它将显示为附件。
    • 我也有同样的问题,能否请您给我一些提示,我应该如何编写 img 标签以避免仅显示为附件?看看这个帖子:stackoverflow.com/questions/5260654/…
    • 什么是fileAttachment,它来自哪里?
    【解决方案3】:

    这对我有用:

      MimeMultipart rootContainer = new MimeMultipart();
      rootContainer.setSubType("related"); 
      rootContainer.addBodyPart(alternativeMultiPartWithPlainTextAndHtml); // not in focus here
      rootContainer.addBodyPart(createInlineImagePart(base64EncodedImageContentByteArray));
      ...
      message.setContent(rootContainer);
      message.setHeader("MIME-Version", "1.0");
      message.setHeader("Content-Type", rootContainer.getContentType());
    
      ...
    
    
      BodyPart createInlineImagePart(byte[] base64EncodedImageContentByteArray) throws MessagingException {
        InternetHeaders headers = new InternetHeaders();
        headers.addHeader("Content-Type", "image/jpeg");
        headers.addHeader("Content-Transfer-Encoding", "base64");
        MimeBodyPart imagePart = new MimeBodyPart(headers, base64EncodedImageContentByteArray);
        imagePart.setDisposition(MimeBodyPart.INLINE);
        imagePart.setContentID("&lt;image&gt;");
        imagePart.setFileName("image.jpg");
        return imagePart;
    

    【讨论】:

    • 请您发布整个代码还是编写文件以及我们需要将提供的代码放入其中的方法?谢谢
    • 您不需要内联和基本编码 - 您可以按照传统方式附加并在@Bernardo 回答的消息文本中添加指向文件的链接
    • 但是记得在附加到主消息之前(在附加文件之后)将 Header 的 Content-Type 设置为 image/jpg 左右
    • 在我最初的帖子之后,我们了解到 base64 内联图像部分应该是“分块”base64。一些带有攻击性病毒扫描程序的邮件服务器拒绝发送带有正常 base64 图像的邮件。
    • @Ujjwal Singh:在我们的例子中,图像源是 html 中的 base64 编码内联图像,因此我们没有考虑将其转换为“传统”文件。我们使用带有内联 base64 图像的 html,因为我们只需将 html 字符串转储到一个文件并使用 firefox 打开它,就可以更轻松地检查生成的电子邮件的布局。
    【解决方案4】:

    如果您使用 Spring,请使用 MimeMessageHelper 发送包含内联内容的电子邮件 (References)。

    创建 JavaMailSender bean 或通过向 application.properties 文件添加相应的属性来配置它,如果您使用的是 Spring Boot

    @Bean
    public JavaMailSender getJavaMailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(host);
        mailSender.setPort(port);
        mailSender.setUsername(username);
        mailSender.setPassword(password);
        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", "smtp");
        props.put("mail.smtp.auth", authEnable);
        props.put("mail.smtp.starttls.enable", starttlsEnable);
        //props.put("mail.debug", "true");
        mailSender.setJavaMailProperties(props);
        return mailSender;
    }
    

    创建算法以生成唯一的CONTENT-ID

    import java.net.InetAddress;
    import java.net.UnknownHostException;
    import java.util.Random;
    
    public class ContentIdGenerator {
    
        static int seq = 0;
        static String hostname;
    
        public static void getHostname() {
            try {
                hostname = InetAddress.getLocalHost().getCanonicalHostName();
            }
            catch (UnknownHostException e) {
                // we can't find our hostname? okay, use something no one else is
                // likely to use
                hostname = new Random(System.currentTimeMillis()).nextInt(100000) + ".localhost";
            }
        }
    
        /**
         * Sequence goes from 0 to 100K, then starts up at 0 again. This is large
         * enough,
         * and saves
         * 
         * @return
         */
        public static synchronized int getSeq() {
            return (seq++) % 100000;
        }
    
        /**
         * One possible way to generate very-likely-unique content IDs.
         * 
         * @return A content id that uses the hostname, the current time, and a
         *         sequence number
         *         to avoid collision.
         */
        public static String getContentId() {
            getHostname();
            int c = getSeq();
            return c + "." + System.currentTimeMillis() + "@" + hostname;
        }
    
    }
    

    使用内联发送电子邮件。

    @Autowired
    private JavaMailSender javaMailSender;
    
    public void sendEmailWithInlineImage() {
        MimeMessage mimeMessage = null;
        try {
            InternetAddress from = new InternetAddress(from, personal);
            mimeMessage = javaMailSender.createMimeMessage();
            mimeMessage.setSubject("Test Inline");
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
            helper.setFrom(from);
            helper.setTo("test@test.com");
            String contentId = ContentIdGenerator.getContentId();
            String htmlText = "Hello,</br> <p>This is test with email inlines.</p><img src=\"cid:" + contentId + "\" />";
            helper.setText(htmlText, true);
    
            ClassPathResource classPathResource = new ClassPathResource("static/images/first.png");
            helper.addInline(contentId, classPathResource);
            javaMailSender.send(mimeMessage);
        }
        catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      可以在此处找到 RFC 规范 (https://www.rfc-editor.org/rfc/rfc2392)。

      首先,email html内容在使用内嵌图片时需要格式如:"cid:logo.png",见:

      <td style="width:114px;padding-top: 19px">
          <img src="cid:logo.png" />
      </td>
      

      其次,MimeBodyPart 对象需要设置属性“disposition”为 MimeBodyPart.INLINE,如下:

      String filename = "logo.png"
      BodyPart image = new MimeBodyPart();
      image.setDisposition(MimeBodyPart.INLINE);
      image.setFileName(filename);
      image.setHeader("Content-ID", "<" +filename+">");
      

      注意,Content-ID 属性必须前后加“”,并且off filename 的值要与src 的内容一致 没有前缀“cid:”的img标签

      最后整个代码如下:

      Message msg = new MimeMessage(session);
      msg.setFrom(new InternetAddress("1234@gmail.com");
      InternetAddress[] recipients = { "123@gmail.com"};
      msg.setRecipients(Message.RecipientType.TO, recipients);
      msg.setSubject("for test");
      msg.setSentDate(new Date());
      
      BodyPart content = new MimeBodyPart();
      content.setContent(<html><body>  <img src="cid:logo.png" /> </body></html>, "text/html; charset=utf-8");
      String fileName = "logo.png";
      BodyPart image = new MimeBodyPart();
      image.setHeader("Content-ID", "<" +fileName+">");
      image.setDisposition(MimeBodyPart.INLINE);
      image.setFileName(fileName);
      InputStream stream = MailService.class.getResourceAsStream(path);
      DataSource fds = new ByteArrayDataSource(IOUtils.toByteArray(stream), "image/png");
      image.setDataHandler(new DataHandler(fds));
      
      MimeMultipart multipart = new MimeMultipart("related");
      multipart.addBodyPart(content);
      multipart.addBodyPart(getImage(image1));
      msg.setContent(multipart);
      msg.saveChanges();
      Transport bus = session.getTransport("smtp");
      bus.connect("username", "password");
      bus.sendMessage(msg, recipients);
      bus.close();
      

      【讨论】:

      • 看起来不错,但getImage(image1) 部分是关于什么的?
      【解决方案6】:

      我在 GMail 和 Thunderbird 中显示内联图像时遇到了一些问题,进行了一些测试并使用以下(示例)代码解决了问题:

      String imagePath = "/path/to/the/image.png";
      String fileName = imagePath.substring(path.lastIndexOf('/') + 1);
      String htmlText = "<html><body>TEST:<img src=\"cid:img1\"></body></html>";
      MimeMultipart multipart = new MimeMultipart("related");
      BodyPart messageBodyPart = new MimeBodyPart();
      messageBodyPart.setContent(htmlText, "text/html; charset=utf-8");
      multipart.addBodyPart(messageBodyPart);
      messageBodyPart = new MimeBodyPart();
      DataSource fds = new FileDataSource(imagePath);
      messageBodyPart.setDataHandler(new DataHandler(fds));
      messageBodyPart.setHeader("Content-ID", "<img1>");
      messageBodyPart.setDisposition(MimeBodyPart.INLINE);
      messageBodyPart.setFileName(fileName);
      multipart.addBodyPart(messageBodyPart);
      message.setContent(multipart);
      

      只是需要注意的一些事情:

      • “Content-ID”必须按照 RFC (https://www.rfc-editor.org/rfc/rfc2392) 中的规定构建,因此它必须是 img 标签 src 属性中的一部分,紧跟在“cid:”之后,用尖括号 ( “”)
      • 我必须设置文件名
      • img 标签中不需要宽度、高度、alt 或标题
      • 我不得不这样放置字符集,因为 html 中的字符集被忽略了

      这对我为某些客户端和 GMail 网络客户端制作内联图像显示很有用,我并不是说这将永远有效! :)

      (对不起我的英语和我的错别字!)

      【讨论】:

        【解决方案7】:

        下面是完整的代码

            import java.awt.image.BufferedImage;
            import java.io.ByteArrayInputStream;
            import java.io.ByteArrayOutputStream;
            import java.io.File;
            import java.io.IOException;
            import javax.imageio.ImageIO;
        
            private BodyPart createInlineImagePart()  {
            MimeBodyPart imagePart =null;
            try
            {
        
                ByteArrayOutputStream baos=new ByteArrayOutputStream(10000);
                BufferedImage img=ImageIO.read(new File(directory path,"sdf_email_logo.jpg"));
                ImageIO.write(img, "jpg", baos);
                baos.flush();
        
                String base64String=Base64.encode(baos.toByteArray());
                baos.close();
        
                byte[] bytearray = Base64.decode(base64String);
                InternetHeaders headers = new InternetHeaders();
                headers.addHeader("Content-Type", "image/jpeg");
                headers.addHeader("Content-Transfer-Encoding", "base64");
                imagePart = new MimeBodyPart(headers, bytearray);
                imagePart.setDisposition(MimeBodyPart.INLINE);
                imagePart.setContentID("&lt;sdf_email_logo&gt;");
                imagePart.setFileName("sdf_email_logo.jpg");
            }
            catch(Exception exp)
            {
                logError("17", "Logo Attach Error : "+exp);
            }
        
            return imagePart;
        }
        
        
        MimeMultipart mp = new MimeMultipart();
         //mp.addBodyPart(createInlineImagePart());
        
        mp.addBodyPart(createInlineImagePart());
        
        String body="<img src=\"cid:sdf_email_logo\"/>"
        

        【讨论】:

          【解决方案8】:

          使用下面的sn-p:

          MimeBodyPart imgBodyPart = new MimeBodyPart();
          imgBodyPart.attachFile("Image.png");
          imgBodyPart.setContentID('<'+"i01@example.com"+'>');
          imgBodyPart.setDisposition(MimeBodyPart.INLINE);
          imgBodyPart.setHeader("Content-Type", "image/png");
          
          multipart.addBodyPart(imgBodyPart);
          

          您不需要内联和基本编码 - 您可以按传统方式附加并将链接添加到类型为 text/html 的主消息文本。
          但是请记住在附加到主消息之前(在附加文件之后)将imgBodyPart 的标头的Content-Type 设置为image/jpg 左右。

          【讨论】:

            猜你喜欢
            • 2021-08-07
            • 2014-03-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-10-06
            • 2015-01-29
            相关资源
            最近更新 更多