{emayili} HTML Messages with Images
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
No two email clients are equal. Nowhere is this more true than in the way that they treat images in HTML messages.
Some clients are fairly permissive. Thunderbird, for example, will happily display images in an HTML message if the images are included in any of the following ways:
- a link to an URL
- a Base64 encoded image or
- an attached image referenced via CID.
Other clients are more restrictive. Gmail, for example, will just completely ignore Base64 encoded images.
If you are composing your HTML email in one of these clients then you really don’t need to worry about this issue because the client will normally just do the right thing and the image will appear in the message regardless of the recipient’s email client.
But if you are building and sending your message via an automated tool, say using {emayili}
in R, then you need to ensure that it will render correctly in all clients. And this means that you can’t use Base64 encoded images. This is not a major restriction. However, if you are generating the contents of the HTML message using R Markdown then most likely any images are going to be Base64 encoded and embedded directly into the HTML body. And that will just not work.
The Problem
Let’s take a look at a couple of examples.
This message (where the image is referenced by URL) should work fine:
To: [email protected] From: [email protected] Content-Type: text/html; charset=utf-8 <html><body><img src="https://www.r-project.org/logo/Rlogo.png"></body></html>
The image referenced in the message body should appear in most email clients. The only potential risk with sending an email like this is that some time in the future the image may no longer be available at the specified URL, in which case it will no longer appear in an email client (unless, of course, there’s a cached local copy).
This message (where the image is Base64 encoded and embedded directly into the HTM) might not work on all clients:
To: [email protected] From: [email protected] Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable <html><body><img src=3D"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAA= 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 BJRU5ErkJggg=3D=3D"></body></html>
The principle merit to sending a message like this is that the image is immutably included in the message itself. So there’s no risk of an image no longer being available at a specific URL or the content of that image being altered. However, none of this means anything if the image is not actually visible in the client!
The Solution
As of version 0.7.2 {emayili}
will handle images in HTML messages in a consistent way, regardless of how those images are referenced in the original HTML. In all cases the images are added as attachments and referenced via CID. This configuration appears to work reliably across all email clients (to the best of my knowledge!).
[1] '0.7.2'
Start by creating the shell of an email message, specifying (fictional) sender and recipient.
msg <- envelope(from = "[email protected]", to = "[email protected]")
Now let’s add an HTML body which references a local image.
msg %>% html('<img src="mini-rlogo.png">') To: [email protected] From: [email protected] Content-Type: multipart/related; boundary="c31cda31" --c31cda31 Content-Type: text/html; charset=utf-8 Content-Disposition: inline <html><body><img src="cid:19cb1165"></body></html> --c31cda31 Content-Type: image/png; name="file55b5a1d689102.png" Content-Disposition: inline; filename="file55b5a1d689102.png" Content-Transfer-Encoding: base64 X-Attachment-Id: 19cb1165 Content-ID: <19cb1165> Content-MD5: 07cypFEtEgmLP/5Wp+PDhw== iVBORw0KGgoAAAANSUhEUgAAACQAAAAcCAYAAAAJKR1YAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAA B3RJTUUH5gENAxwsFSostQAABRFJREFUSMetl3tsVEUUh397W1opiY+WACII8gcoEIoKEQgmxNjG DZTybOxLwFQgiBUIIQQkCDEkaNQIiRqQCIhQSgvhIdgEoQ+gBCMBBKIRCRKhq6Yij/LI0vP5x+6W u927pStOcrI3s2fmfPc358zM9SmBdqC67hFgGOhZRD9BTyAD6Eio3cJoBC4a/AwcB47m5vhvtDeG 734OB2sOpUvKB/JAI4BkQAlYEKjDKAfKJk7IufqfgKprD3dHWiSYBqS1G8AQKN7/N4A1wMq8yeP+ bBdQTe2RJMQcxDJQp1CA/9mMq8Bioc/y8ydaXKDq2sMZSGWClxMLImHcARqB24ADdAIeu88S78Yo KirKuxYDdLDmcFeJA0j970kf1xDUI6pA9cBp4I/cHH/U21ZU7kwCdQOeAYYC2Wb2IijJNdcPQNaU 11690gJ0oObQQz6pNjyoLZDroE8lPh/tz76QSIVG2tebt/UA3gpbx/C81RhZ06YV3k2WJMG7Jg1t SxWJrUJzRvuzApLUb3ZdOmKMIDSFCD9G/EN9inrWzXeqOYVPC5eP/PuLcOUNBkYBCySt8H13sK6n xDkgJd7ygOY6YpXfn03kTfvOrs2UdCIKIAQeA+bRd0pi0dIRjYeAGiATowno7YC9bkaKGfIyYPFo f9YnbphIkLZgQuYJI2AQsGfp4fRijHyMu+EiKHaAbDB5G8cwrYyXD/Fh5IKIVasFFj5adiQ9aGbb wwJkO2b0jauO2aoxo7MtcZi2ls7tow7yqdBgb1iEvg5YikuRKDM42lbFxIFpltQxYkBHSSMQja1g InEGYHYuLEJKshkNwMOelYWCCcJIgkdNd46vyXLnXH2vN/btkTSltbJCSWY0h9VrcMDqPc8jQ4YN akOfuMt0VpejPHuV7HNAfWNgQiPOg/UMpQj1yWZsBqbGqoOAkvLy3d/k5eUQgxM/Z5K6qdsKTa+K 5IxPaLjE8BiY0CrsMKM4PH5zsoz9iCPACI+NMVc+Gydph5dCcRLYJ2nhvb7wpugBg1S2YPClX0AF girkO+bk5LwCZtMxuxmn9L/aUlb5UnvyqI2NMFYZaWOPTndKgLXhy92sOXNm0nK4Vm7fPQEoB5I8 EjwILAY+LirMuytJfWbuz5Q4kQgMUkBQKWnj/EG/n5G0DhgvaezcubOqpFbXj20VOycAm1yHnuvS hYDThi0H7Vhe33kARB8dLphbgCMp1UOZo5npN7ZlPXFlFlIXIG/evDe/jTA4bqDJk3K3m9lwMztj ZmoxWmxgSEW7WPD0lSX3jocomCu/rfWnIaUhlrSCETDsZGPah79eS70FvOCGiVEo0jZvqUgFSjEW Aulee1SgKUlrT2fE5FEHx4LzMht2AWlmdN56LmPohespXjl3EjGkYf34u+7YjhdQQf6kO4UFkz8A ewp427AfXSrJoq8lUTnjiA5mTDTDDwwd1f0f+RTrK5EpNKN17Pt+dUjSl+s3+UD9AD/GSOC5QFPy k+vOZjitEzg1qVmlAy8b6AKhz6C61acfH3W72TfefROILC9Sv8CGCX8lBOTV+s/aO6Qp6Hwfc5LD 1fmZl7qVls64HfHtPnVXH/n4SdAhWlFJYk1g48QWpZyEKFytKegEvSoMydwwknR5/djzgjUeMBKU dC2ueP6BgdrYe7xVh/dATa0vcUiOxOouReXOAwEBQUkNEg0Q+aVBUsDL//KG8QGJ94UCEgFBwDWu t6QcSfoXYMm8g9eHpwkAAAAldEVYdGRhdGU6Y3JlYXRlADIwMjItMDEtMTNUMDM6Mjg6MTYrMDA6 MDACS/DEAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEwLTEzVDE1OjEwOjAyKzAwOjAwc8UBSAAA AABJRU5ErkJggg== --c31cda31--
The resulting message is a little more complicated. Instead of simply having an HTML body, the message now has a multipart/related
MIME body which includes
- an HTML body with an
<img>
tag which references an image via CID and - a Base64 encoded attachment which is labelled via the
Content-ID
header.
So that’s how it works with a local image. You can equally reference an image by URL.
msg %>% html('<img src="https://www.r-project.org/logo/Rlogo.png">')
In this case the image is downloaded from the URL and then attached to the message as above.
You can also specify a Base64 encoded image.
BASE64_IMG <- "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAGCAMAAAA40H\ REAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3Ccu\ lE8AAAAflBMVEUAAADAwsW+wMO6vL+1t7uwsraqq7Clpqu9v8K5u7+osLxAeb45dLtliLOfoKa3uby0\ tbkla8AiaLsfZbdmg6ewsrarrbElar0fZbgdY7VsgqGqq7ClpquNl6hKdqsjZLBtgZ1LeK8eZLVVeaY\ ZX68XXawjab4hZ7sgZrj////8ro41AAAAJnRSTlMABn3Z9dl7Bp/xbnRwmob3dR+d9rrBox+364Qat9\ Da6SMv+ibTdewDpr4AAAABYktHRCnKt4UkAAAAB3RJTUUH5gENBBEUjcn84wAAADhJREFUCNdjYGRiZ\ mFlY2fg4OTi5uHlY+AXEFQTEhZhEBUTV5eQlGKQlpHVkJNXYGBgUFRSVlEFADk9AzXF1ilQAAAAJXRF\ WHRkYXRlOmNyZWF0ZQAyMDIyLTAxLTEzVDAzOjI4OjE2KzAwOjAwAkvwxAAAACV0RVh0ZGF0ZTptb2R\ pZnkAMjAxNy0xMC0xM1QxNToxMDowMiswMDowMHPFAUgAAAAASUVORK5CYII=" msg %>% html('<img src="{{BASE64_IMG}}">') Date: Thu, 13 Jan 2022 04:07:18 GMT X-Mailer: {emayili}-0.7.2 MIME-Version: 1.0 To: [email protected] From: [email protected] Content-Type: multipart/related; boundary="3b7fb415" --3b7fb415 Content-Type: text/html; charset=utf-8 Content-Disposition: inline <html><body><img src="cid:70c51a8a"></body></html> --3b7fb415 Content-Type: image/png; name="file55b5aed91fa3.png" Content-Disposition: inline; filename="file55b5aed91fa3.png" Content-Transfer-Encoding: base64 X-Attachment-Id: 70c51a8a Content-ID: <70c51a8a> Content-MD5: vDyY8WtQoSM1M8sl8EBZ/g== iVBORw0KGgoAAAANSUhEUgAAAAcAAAAGCAMAAAA40HREAAAABGdBTUEAALGPC/xhBQAAACBjSFJN AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAflBMVEUAAADAwsW+wMO6vL+1 t7uwsraqq7Clpqu9v8K5u7+osLxAeb45dLtliLOfoKa3uby0tbkla8AiaLsfZbdmg6ewsrarrbEl ar0fZbgdY7VsgqGqq7ClpquNl6hKdqsjZLBtgZ1LeK8eZLVVeaYZX68XXawjab4hZ7sgZrj////8 ro41AAAAJnRSTlMABn3Z9dl7Bp/xbnRwmob3dR+d9rrBox+364Qat9Da6SMv+ibTdewDpr4AAAAB YktHRCnKt4UkAAAAB3RJTUUH5gENBBEUjcn84wAAADhJREFUCNdjYGRiZmFlY2fg4OTi5uHlY+AX EFQTEhZhEBUTV5eQlGKQlpHVkJNXYGBgUFRSVlEFADk9AzXF1ilQAAAAJXRFWHRkYXRlOmNyZWF0 ZQAyMDIyLTAxLTEzVDAzOjI4OjE2KzAwOjAwAkvwxAAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxNy0x MC0xM1QxNToxMDowMiswMDowMHPFAUgAAAAASUVORK5CYII= --3b7fb415--
Conclusion
This new feature should make it substantially easier to formulate HTML email messages which include images. I’ve tested the new version of {emayili}
fairly extensively, but I’m fully anticipating that there will still be a few minor problems. Please create an issue on the repository if you run into any trouble.
Finally, this new feature may appear a little arbitrary, but it forms the basis of the next new feature which I’ve been working on. And that I’m really excited about. Expect to hear more in the next few weeks.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.