Send email node with inline images (for gmail)

Answering my own question, it’s possible but complex.

  1. Sort table in the desired order

  2. convert your pngs to binary objects
    (there would probably be a different option of doing it by saving them to disk)

  3. Java Snippet
    This does 2 things.
    It converts the binary objects(pngs) to base64 and it replaces (or adds) a column that will be used to contains the images in the html table. The content is alreay html (as string).

     try {
    
     ByteArrayOutputStream buffer = new ByteArrayOutputStream();
     
     int nRead;
     byte[] data = new byte[4096];
     
     while ((nRead = c_Structure.read(data, 0, data.length)) != -1) {
       buffer.write(data, 0, nRead);
     }
     
     out_base64_image = Base64.getEncoder().encodeToString(buffer.toByteArray());
     out_html_image = "<img src=\"cid:image-" + ROWINDEX + "\">";	
     
     } catch (IOException ex) {
         throw new Abort(ex);
     }
    

cid = content id which is needed to display attached images inline of the email. So the content id simply is image-0, image-1 etc. Now you see why sorting at the start is required.

  1. Table to HTML node and list of base64 images

The Table to html node is used to convert the table to html. A second branch uses groupby node without group column to generate a list column containing all the base64 encoded pngs. Then the 2 branches are joined again with column appender.
This is done because the email is sent in a java snippet and hence we only want 1 row to send the email only once. Same is done for email adresses. they are appended as a list column to this table. This is a good point to show the workflow as it’s probably getting confusing by now:

image

  1. Java Snippet to send email

Here we use java mail to send the email. You will need the javax.mail.jar with must be added as additional library to the snippet. The code inside the snippet see below:

try {	
    
    // adjust accordingly to your smtp server    
    Properties mailProps = new Properties();
    mailProps.put("mail.transport.protocol", "smtp");
    mailProps.put("mail.host", "smtp.mydomain.com");
    mailProps.put("mail.smtp.port", "25");
    mailProps.put("mail.smtp.auth", "false");

    Session session = Session.getDefaultInstance(mailProps);
    Message m = new MimeMessage(session);
    MimeMultipart content = new MimeMultipart();
    MimeBodyPart mainPart = new MimeBodyPart();
    mainPart.setText(c_HTML, "UTF-8", "html"); // set html table as main email text
    content.addBodyPart(mainPart);  
    
    // attach all the images
    int idx = 0;
    for (String b64 : c_base64_image) { // this is the list of base64 images

        MimeBodyPart imgPart = new PreencodedMimeBodyPart("base64"); // we already have base64 encoded images
        imgPart.setContent(b64, "image/png");
        imgPart.setContentID("<image-" + idx + ">"); // this is the cid from first java snippet
        imgPart.setDisposition(MimeBodyPart.INLINE); // tell that the images are displayed inline
        content.addBodyPart(imgPart);
        idx++;
    }
            
    m.setContent(content);
    m.setSubject("My Subject");
    m.setFrom(new InternetAddress("knime@knime.com"));
    
    for (String email : c_email) { // list of email addreses
        m.addRecipient(RecipientType.TO, new InternetAddress(email));
    }
    
    // It seems knime/eclipse already has loaded some different java mail library
    // This solution to that problem was found on Stackoverflow
    // if this is omitted sending fails with a nonsensical/confusing error message
    Thread t =  Thread.currentThread();
        ClassLoader ccl = t.getContextClassLoader();
    t.setContextClassLoader(session.getClass().getClassLoader());
      try {      	
        Transport.send(m);
    } finally {
        t.setContextClassLoader(ccl);
    }
    
} catch (MessagingException ex) {
    throw new Abort(ex);
} 

This will send your knime table per email with images correctly displayed inside the table.

6 Likes