Sven Tenscher
Sven Tenscher

Reputation: 195

Python Requests PUT to update image in Prestashop

I'm trying to update an existing image from a product in prestashop. I'm using Python and Requests and the following code:

import requests
import io
import mimetypes
from PIL import Image
from StringIO import StringIO

api_key = 'test'

url = "https://.../api/images/products/249/445"

file_name = 't3_6kxvzv.jpg'
fd = io.open(file_name, "rb")
content = fd.read()
fd.close()

def encode_multipart_formdata():
    """Encode files to an http multipart/form-data.
    :param files: a sequence of (type, filename, value)
        elements for data to be uploaded as files.
    :return: headers and body.
    """
    BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
    CRLF = '\r\n'
    L = []
    L.append('--' + BOUNDARY)
    L.append(
        'Content-Disposition: form-data; \
            name="%s"; filename="%s"' % ("image", file_name))
    L.append('Content-Type: %s' % get_content_type(file_name))
    L.append('')
    L.append(content)
    L.append('--' + BOUNDARY + '--')
    L.append('')
    body = CRLF.join(L)
    headers = {
         'Content-Type': 'multipart/form-data; boundary=%s' % BOUNDARY
    }
    return headers, body

def get_content_type(file_name):
    """Retrieve filename mimetype.
    :param filename: file name.
    :return: mimetype.
    """
    return mimetypes.guess_type(file_name)[0] or 'application/octet- stream'

header, body = encode_multipart_formdata()

r = requests.put(url, data=body, auth=(api_key,""), headers= header)
# also tried data = content

r = requests.get(url, auth=(api_key,""))
i = Image.open(StringIO(r.content))
i.show()

I tried various PUT and POST requests with

data = content 

but getting only a 400 status code.

I then tried to GET the existing image, which works fine.

The api_key has all the necessary setting to allow PUT and POST.

I then tried to read into how prestapyt is solving this problem, however after importing prestapyt I couldn't follow their documentation to add an image to a product using:

prestashop.add("https://...api/images/products/249/445", files[('image',file_name,content)])

produces:

KeyError: ('image', 't3_6kxvzv.jpg', '\xff\xd8\xff\xe0\x00\x10JFI...

I tried then to modify the encode_multipart_formdata and get_content_type functions to produce a similar solution, but cannot get past the 400 status code.

I would very much prefer to use Requests and try to understand how to update a picture to using prestapyt and a turn-key solution.

Thank you for your time!

Documentation I used: Prestashop http://doc.prestashop.com/display/PS16/Chapter+9+-+Image+management

prestapyt https://github.com/prestapyt/prestapyt

UPDATE:

I was able to use Requests and POST to add an image to a product via:

url_2 = "https:/.../api/images/products/249"
r = requests.post(url_2, data=body, auth=(api_key,""), headers=header)

Still not able to use PUT to change or update an image.

Upvotes: 1

Views: 1105

Answers (2)

Sergio P.
Sergio P.

Reputation: 68

This answer comes a little bit late, but here it is. You're reinventing the wheel, altough it's being interesting seeing how: now I understand how to build a multipart form data from scratch. I tried your code and it fails since youre joining str and bytes in your encode_multipart_formdata function:

L.append(content)

That line will raise a TypeError exception.

Requests can post multipart form data in a very simple way:

files = {'image': ('../imagepath/imagename.jpg', open('../imagepath/imagename.jpg', 'rb'), 'image/jpg')}
body, content_type = requests.models.RequestEncodingMixin._encode_files(files, {})

headers = {
    "Content-Type": content_type
}

r=requests.post(  url + "images/products/" + str(product_id),data=body, headers=headers)

print(r.text)

This has been tested with Python 3.7 and PrestaShop 1.7.8.2.

Upvotes: 1

Sven Tenscher
Sven Tenscher

Reputation: 195

Couldn't get PUT to work, so instead used DELETE and POST

import requests
import io
import mimetypes
import xml.etree.ElementTree as ET
import sys

api = ''

urls =["https://.../api/images/products/22",
       "https://.../api/images/products/31",
       "https://.../api/images/products/37",
       "https://.../api/images/products/46",
       "https://.../api/images/products/212"]


def encode_multipart_formdata(file_name,content):
    """Encode files to an http multipart/form-data.
    :param files: a sequence of (type, filename, value)
         elements for data to be uploaded as files.
    :return: headers and body.
    """
    BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
    CRLF = '\r\n'
    L = []
    L.append('--' + BOUNDARY)
    L.append(
         'Content-Disposition: form-data; \
            name="%s"; filename="%s"' % ("image", file_name))
    L.append('Content-Type: %s' % get_content_type(file_name))
    L.append('')
    L.append(content)
    L.append('--' + BOUNDARY + '--')
    L.append('')
    body = CRLF.join(L)
    headers = {
        'Content-Type': 'multipart/form-data; boundary=%s' % BOUNDARY
    }
    return headers, body


def get_content_type(file_name):
    """Retrieve filename mimetype.
    :param filename: file name.
    :return: mimetype.
    """
    return mimetypes.guess_type(file_name)[0] or 'application/octet-stream'


def get_image_url(url):
    """get from a given url the image url"""
    r = requests.get(url, auth=(api,""))
    tree = ET.fromstring(r.content)
    return tree.find("image").find("declination").get("{http://www.w3.org/1999/xlink}href")


 def delete_image(url):
    """deletes the image on prestashop given by url"""
    url2 = get_image_url(url)
    requests.delete(url2, auth=(api,""))


def load_image(file_name):
    """loads image to upload"""    
    fd = io.open(file_name, "rb")
    content = fd.read()
    fd.close()
    return content, file_name


def upload_image(url, file_name):
    """uploads new image to a given url"""
    content, file_name = load_image(file_name)
    header, body = encode_multipart_formdata(file_name, content)
    requests.post(url, data=body, auth=(api,""), headers=header)

if __name__ == "__main__":
    file_name = sys.argv[1]
    content, file_name = load_image(file_name)
    for i in urls:
        delete_image(i)
        upload_image(i,file_name)

Workaround works fine, still don't understand why there has to be such a complicated way and why PUT doesn't work.

Upvotes: 0

Related Questions