2

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.

Jack
  • 77
  • 9
  • PIL `Image` objects have a [`getdata()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getdata) method that will allow sequential access its pixel values, so it could probably be used to do what you want. Here's an [example](https://stackoverflow.com/a/40729543/355230) of using it (to do something else). I suggest you add some sample code to your question showing how you would like to use this `read()` method. – martineau Apr 21 '19 at 19:08
  • added some code to show what I want to do with the image, hope it helps. – Jack Apr 21 '19 at 19:45

2 Answers2

3

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.

Fabian
  • 1,130
  • 9
  • 25
2

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())

...
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Can you explain the difference between this approach with `.seek(0)` and `ramfile.read()` and the approach above with `b64encode()` and `ram_file.getvalue()`? – physicalattraction Apr 22 '19 at 20:26
  • 1
    I think it will make the size of the attachment smaller since there's no Base64 encoding being done to the image data (which isn't necessary when using `octet-stream`) — plus `ram_file` has the `read()` method which is what you requested. – martineau Apr 22 '19 at 20:51