I want to handle a screenshot as an opened file, it more specifically has to have the attribute .read().

I'm taking the screenshot using PIL's ImageGrab.grab() which gives me an Image-Object, which obviously doesn't have the .read() attribute. I know that I could save the image and reopen it with open("image.png", "rb") but I would like to know if there is an easy way to do this without having to save the image in the process.

I want to add the image to an email with the help of smtplib. Usually, I would do this:

filename='filename'
attachment  =open(filename,'rb')

part = MIMEBase('application','octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment; filename= "+filename)

msg.attach(part)

but in this case, I want to use the screenshot taken before.

2 Answers

3
Fabian On

You can use base64 and BytesIO to store it as a buffered image.

import base64
from io import BytesIO

buffered_img = BytesIO()
image.save(buffered_img, format="JPEG")
img_str = base64.b64encode(buffered_img.getvalue())

Then use the img_str.

2
martineau On

Yes, there is. Assuming you want the image in some specific, possibly compressed, standard image file format—as opposed to just the raw pixel values—you could do it by first saving the screenshot into a BytesIO memory file and then "rewind" that in preparation of the subsequent read() call.

Something along these lines:

from email.mime.base import MIMEBase
from io import BytesIO
from PIL import ImageGrab


file_format = 'PNG'
filename = 'screenshot.png'

img_grab = ImageGrab.grab()  # Take a screen snapshot.

ram_file = BytesIO()
img_grab.save(ram_file, format(file_format))  # Save in desired format.
ram_file.seek(0)  # Rewind for reading.

part = MIMEBase('application', 'octet-stream')
part.set_payload(ram_file.read())

...