Brandon
Brandon

Reputation: 7966

How do I post an Image object with a multi-part POST with Request?

I'm trying to convert some javascript code I wrote to Python, but am getting stuck on passing data b/t PIL and Requests objects.

My python script downloads an image to memory:

from PIL import Image
import urllib2
import cStringIO

def fetch_image_to_memory(url):
   req = urllib2.Request(url, headers={
                         'User-Agent': "Mozilla / 5.0 (X11; U; Linux i686) Gecko / 20071127 Firefox / 2.0.0.11"})
   con = urllib2.urlopen(req)
   imgData = con.read()
   return Image.open(cStringIO.StringIO(imgData))

I'd then like to add it to the form data for a POST operation. This code succeeds when the file is on disk:

from requests_toolbelt import MultipartEncoder
import requests
url = 'https://us-west-2.api.scaphold.io/graphql/some-gql-endpoint'

multipart_data = MultipartEncoder(
    fields={
         'query':'some-graphql-specific-query-string',
        'variables': '{ "input": {"blobFieldName": "myBlobField" }}',

        ## `variables.input.blobFieldName` must hold name 
        ## of Form field w/ the file to be uploaded
        'type': 'application/json',
        'myBlobField': ('example.jpg', img, 'image/jpeg')
    }
)
req_headers = {'Content-Type':multipart_data.content_type, 
             'Authorization':'Bearer secret-bearer-token'}
r = requests.post(url, data=multipart_data, headers=req_headers)

However, when trying to pass in an Image object from the fetch_image_to_memory function:

 'myBlobField': ('example.jpg', image_object, 'image/jpeg')

...I get the error:

Traceback (most recent call last):
  File "test-gql.py", line 38, in <module>
    'myBlobField': img
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 119, in __init__
    self._prepare_parts()
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 240, in _prepare_
parts
    self.parts = [Part.from_field(f, enc) for f in self._iter_fields()]
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 488, in from_fiel
d
    body = coerce_data(field.data, encoding)
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 466, in coerce_da
ta
    return CustomBytesIO(data, encoding)
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 529, in __init__
    buffer = encode_with(buffer, encoding)
  File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 410, in encode_wi
th
    return string.encode(encoding)
AttributeError: 'JpegImageFile' object has no attribute 'encode'

I know from the open() docs that it returns an object of type file, but the only way I can see in PIL to convert from Image to file is by using save(), which writes it to the disk. I can write to disk, but I'd rather avoid the step, since I am handling a lot of images.

Is it possible to convert the Image object to file type? Or some other workaround with similar effect?

Upvotes: 2

Views: 2404

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1123510

MultipartEncoder can take a byte string or a file object, but a PIL image object is neither.

You'd have to create an in-memory file object first:

from io import BytesIO

image_file = BytesIO()
img.save(image_file, "JPEG")
image_file.seek(0)

then use image_file in the post:

'myBlobField': ('example.jpg', image_file, 'image/jpeg')

Upvotes: 4

Related Questions