I have a message in HTML format. I need to reply to this email, keeping all the pictures from the previous email.
I have all the pictures from the email saved, and now I need to add them back. How to do it?
exchabgelib has instructions for this:
from exchangelib import HTMLBody
message = Message()
logo_filename = 'logo.png'
with open(logo_filename, 'rb') as f:
my_logo = FileAttachment(
name=logo_filename, content=f.read(), is_inline=True,
content_id=logo_filename
)
message.attach(my_logo)
# Most email systems
message.body = HTMLBody(
'<html><body>Hello logo: <img src="cid:%s"></body></html>' % logo_filename
)
If the new email must also be in HTML format, then attach the images one by one and add an <img> tag for each image you have attached. Otherwise, just attach the images and compose the email in plain text.
This question is really a continuation of this answer
https://stackoverflow.com/a/49098251/19308674. I'm trying to add multiple embedded images (not just one) to the email content.
I want to do it in a way that I loop through a list of images, in addition, there will be different text next to each image. Something like this for example as you can see in Weather Next 10 days I want to loop through images from a folder and next to each image there will be some different text as in the example.
from email.message import EmailMessage
from email.utils import make_msgid
import mimetypes
msg = EmailMessage()
# generic email headers
msg['Subject'] = 'Hello there'
msg['From'] = 'ABCD <abcd#example.com>'
msg['To'] = 'PQRS <pqrs#example.org>'
# set the plain text body
msg.set_content('This is a plain text body.')
# now create a Content-ID for the image
image_cid = make_msgid(domain='example.com')
# if `domain` argument isn't provided, it will
# use your computer's name
# set an alternative html body
msg.add_alternative("""\
<html>
<body>
<p>This is an HTML body.<br>
It also has an image.
</p>
<img src="cid:{image_cid}">
</body>
</html>
""".format(image_cid=image_cid[1:-1]), subtype='html')
# image_cid looks like <long.random.number#example.com>
# to use it as the img src, we don't need `<` or `>`
# so we use [1:-1] to strip them off
# now open the image and attach it to the email
with open('path/to/image.jpg', 'rb') as img:
# know the Content-Type of the image
maintype, subtype = mimetypes.guess_type(img.name)[0].split('/')
# attach it
msg.get_payload()[1].add_related(img.read(),
maintype=maintype,
subtype=subtype,
cid=image_cid)
# the message is ready now
# you can write it to a file
# or send it using smtplib
If I'm able to guess what you are trying to ask, the solution is simply to generate a unique cid for each image.
from email.message import EmailMessage
from email.utils import make_msgid
# import mimetypes
msg = EmailMessage()
msg["Subject"] = "Hello there"
msg["From"] = "ABCD <abcd#example.com>"
msg["To"] = "PQRS <pqrs#example.org>"
# create a Content-ID for each image
image_cid = [make_msgid(domain="example.com")[1:-1],
make_msgid(domain="example.com")[1:-1],
make_msgid(domain="example.com")[1:-1]]
msg.set_content("""\
<html>
<body>
<p>This is an HTML body.<br>
It also has three images.
</p>
<img src="cid:{image_cid[0]}"><br/>
<img src="cid:{image_cid[1]}"><br/>
<img src="cid:{image_cid[2]}">
</body>
</html>
""".format(image_cid=image_cid), subtype='html')
for idx, imgtup in enumerate([
("path/to/first.jpg", "jpeg"),
("file/name/of/second.png", "png"),
("path/to/third.gif", "gif")]):
imgfile, imgtype = imgtup
with open(imgfile, "rb") as img:
msg.add_related(
img.read(),
maintype="image",
subtype=imgtype,
cid=f"<{image_cid[idx]}>")
# The message is ready now.
# You can write it to a file
# or send it using smtplib
Kudos for using the modern EmailMessage API; we still see way too many questions which blindly copy/paste the old API from Python <= 3.5 with MIMEMultipart etc etc.
I took out the mimetypes image format quessing logic in favor of spelling out the type of each image in the code. If you need Python to guess, you know how to do that, but for a small static list of images, it seems to make more sense to just specify each, and avoid the overhead as well as the unlikely but still not impossible problem of having the heuristics guess wrong.
I'm guessing your images will all use the same format, and so you could actually simply hardcode subtype="png" or whatever.
It should hopefully be obvious how to add more per-image information into the loop over image tuples, though if your needs go beyond the trivial, you'll probably want to encapsulate the image and its various attributes into a simple class.
Your message apparently makes no sense for a recipient who cannot access the HTML part, so I took out the bogus text/plain part you had. You were effectively sending a different message entirely to recipients whose preference is to view plain text over HTML; if that was genuinely your intent, please stop it. If you are unable to provide the same information in the plain text version as in the HTML version, at least don't make it look to those recipients like you had nothing of importance to say in the first place.
Tangentially, please don't fake email addresses of domains you don't own. You will end up tipping off the spammers and have them trying to send unsolicited messages to an innocent third party. Always use IANA-reserved domains like example.com, example.org etc which are guaranteed to never exist in reality. I edited your question to fix this.
My goal is to send a picture via POST request in Python and to display it in a dashboard in Node Red, but I'm stuck in the step of reading the image once it's been sent.
This is the piece of code I'm using to send the file:
directory = os.path.join(DIRECTORY_IMAGES, id)
files = {'image': open(directory,'rb')}
requests.post('http://XXX.XXX.X.XX:1880/IMAGE', files = files)
Once in Node-red, the blocks I'm using are the following:
The message is received, but I'm unable to get the picture out of it. As I've read in other posts the msg.payload is set to req.files[0].buffer, encoded as Base64 and displayed using <img width="16" height="16" src="files:image;base64,{{msg.payload}}" /> in a template, but that does not display the image.
[{"id":"797939a8.a91a58","type":"http response","z":"ddfa6621.2e7168","name":"","statusCode":"","headers":{},"x":1290,"y":2280,"wires":[]},{"id":"d5b145ce.a712f8","type":"ui_template","z":"ddfa6621.2e7168","group":"9beeae56.34305","name":"Display image","order":4,"width":"7","height":"6","format":"<img width=\"16\" height=\"16\" src=\"files:image;base64,{{msg.payload}}\" />\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":1680,"y":2220,"wires":[[]]},{"id":"5e30e496.958b9c","type":"change","z":"ddfa6621.2e7168","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"req.files[0].buffer","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1300,"y":2220,"wires":[["2f260e60.d12642"]]},{"id":"2f260e60.d12642","type":"base64","z":"ddfa6621.2e7168","name":"Encode","x":1500,"y":2220,"wires":[["d5b145ce.a712f8"]]},{"id":"18c71628.986f4a","type":"http in","z":"ddfa6621.2e7168","name":"","url":"IMAGE","method":"post","upload":true,"swaggerDoc":"","x":1050,"y":2220,"wires":[["5e30e496.958b9c","797939a8.a91a58"]]},{"id":"9beeae56.34305","type":"ui_group","z":"","name":"Última pierna procesada","tab":"223d38a0.9cbaa8","order":8,"disp":true,"width":"7","collapse":false},{"id":"223d38a0.9cbaa8","type":"ui_tab","z":"","name":"CALIDAD PULPAS CINTA 0","icon":"dashboard","disabled":false,"hidden":false}]
What am I missing?
The img src tag should start with data:image/jpeg;base64, (assuming a JPEG image)
You should swap the image/jpeg for the mime type of the image, e.g. image/png
[{"id":"9dff2c2e.66ea1","type":"http response","z":"c09a8743.c89388","name":"","statusCode":"","headers":{},"x":390,"y":180,"wires":[]},{"id":"68e0a720.f29498","type":"ui_template","z":"c09a8743.c89388","group":"efcf5006.15dae","name":"Display image","order":4,"width":"7","height":"6","format":"<img width=\"16\" height=\"16\" src=\"data:image/jpeg;base64,{{msg.payload}}\" />\n","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":800,"y":120,"wires":[[]]},{"id":"2fb71bea.a5d1e4","type":"http in","z":"c09a8743.c89388","name":"","url":"IMAGE","method":"post","upload":true,"swaggerDoc":"","x":210,"y":120,"wires":[["9dff2c2e.66ea1","a87b8d6.a9b0c7"]]},{"id":"a87b8d6.a9b0c7","type":"change","z":"c09a8743.c89388","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"req.files[0].buffer","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":120,"wires":[["4bcddbb.38c2624"]]},{"id":"4bcddbb.38c2624","type":"base64","z":"c09a8743.c89388","name":"Encode","x":620,"y":120,"wires":[["7d7586e7.205398","68e0a720.f29498"]]},{"id":"7d7586e7.205398","type":"debug","z":"c09a8743.c89388","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":60,"wires":[]},{"id":"efcf5006.15dae","type":"ui_group","z":"","name":"Última pierna procesada","tab":"65324def.436554","order":8,"disp":true,"width":"7"},{"id":"65324def.436554","type":"ui_tab","z":"","name":"CALIDAD PULPAS CINTA 0","icon":"dashboard"}]
triggered with the following python:
import requests
files = {'image': open('/Users/hardillb/temp/photos/DSC_7188.JPG', 'rb')}
requests.post('http://localhost:1880/IMAGE', files = files)
I am generating html files using elementtree.ElementTree.dump on an Element. The files look ok in all browsers, and the underlying code within the files looks fine (no unclosed brackets or anything).
When I send an email to Outlook 2010 via smtplib, I am seeing weird formatting issues. These issues will be 100% repeatable, so the issue is logical. Here is an example:
<table b="" order="1">
That is from the source code of a HTML email I sent myself. It is correctly written as:
<table border="1">
within the original source code.
If in Outlook I write a HTML email using the original HTML as source, it correctly formats. (New email-attach html file->insert as text)
Is the issue going to be Outlook or Python? The function I used for reading the html file and sending is below.
def email_Report(mailOptions):
reportName = time.strftime("%Y%m%d.%H%M") + ".html"
ElementTree(mailOptions['report']).write("/home/%s/%s" %(mailOptions['username'],reportName))
#Set sender and receiver to the user building the report.
mailaddr = '%s#acme.com' %(mailOptions['username'])
#Access the report file. Added binary in case we ever use code on Windows
filename = "/home/%s/%s" % (mailOptions['username'], reportName)
open_file = open(filename, 'rb')
emsg = MIMEText(open_file.read(), 'html')
open_file.close()
emsg['Subject'] = "Report for %s generated by %s %s" % (mailOptions['zone'], mailOptions['username'], time.strftime("%d%m%Y-%H%M"))
emsg['To'] = mailaddr
emsg['From'] = mailaddr
#Hostname can be a parameter to SMTP method if localhost isn't listening
sc = smtplib.SMTP()
sc.connect()
sc.sendmail(mailaddr, mailaddr, emsg.as_string())
sc.close()
return
The HTML is extremely simple. No CSS, no title or head tags etc. Just html->body->table->tr->th->(newrow)->td->td etc. Could I have overlooked something like encoding/escaping? Do I have to use mime multipart? I am using Python 2.4.3 and can't use any module that didn't come stock.
Are you sure you're not running into the 990 character limit for mail servers as per
workaround for the 990 character limitation for email mailservers
My bad. Postmark does not support inline images apparently. Solved by changing smtp-mail provider.
I'm trying to send e-mails with TurboMail using pylons.
Everything works fine, except for using embedded images in html-content. It seems that the Content-ID header for each image is being lost somewhere along the way.
This is my code:
def sendMail(to,subject,html_content,plain_content,images):
from turbomail import Message as Mail
mail = Mail(to=to,subject=subject)
mail.plain = plain_content
mail.rich = html_content
for cid,path in images.iteritems():
mail.embed(path,cid)
mail.send()
In my tests the html content is:
<html>
<header/>
<body>
<h1>Send images using TurboMail</h1>
<img src="cid:img0" />
</body>
</html>
And the images dict:
{"img0":"path/to/img0"}
When you pass in both a filename and a cid, TurboMail ignores the cid and uses the basename of the file instead. I suspect your filenames have extensions and your cids do not:
{"img0":"path/to/img0.png"}
If so, the images are embedded with a cid of img0.png instead.
You could pass in an open image file instead; TurboMail will then not ignore the name:
def sendMail(to,subject,html_content,plain_content,images):
from turbomail import Message as Mail
mail = Mail(to=to,subject=subject)
mail.plain = plain_content
mail.rich = html_content
for cid,path in images.iteritems():
mail.embed(open(path, 'rb'), cid)
mail.send()
I'd use marrow.mailer instead; it's the new name for the same package but the .embed method has been made a little saner in it's handling of embedded images and cids.
an earlier revision of this answer had marrow and TurboMail confused, referring to the marrow .embed signature instead.
Apparently, Postmarkapp does not support inline images.