Hi. I have a running API that receives an image posted from JavaScript component. I would like to store the image in anvil and return a link to the stored image. The data is posted as multipart/form-data and I can only control the receiving end. When storing the body as is it looks like this in the database:
… and will not display as an image when returned as a image link. I guess I need to store only the image part of the posted data, but I have no idea how I can achieve this. I have tried various examples posted here but without luck so far. The output from print(“multipart_bytes”, multipart_bytes) looks like this:
b'------WebKitFormBoundary7OM0q7aB7U0ghqhi\r\nContent-Disposition: form-data; name="image"; filename="2021-04-08_14-00_1.png"\r\nContent-Type: image/png\r\n\r\n\x89PNG\r\n\x1a\n\x00\x.......
Server code :
@anvil.server.http_endpoint("/images/store_for_editor_js", methods=["POST"], enable_cors=True, cross_site_session=True)
def upload_image2(**params):
content_type_string = anvil.server.request.headers['content-type']
id = str(uuid.uuid4())
file_size = anvil.server.request.headers['content-length']
try:
ip_address = anvil.server.request.headers['x-forwarded-for']
except:
ip_address = "unknown"
multipart_bytes = anvil.server.request.body.get_bytes()
print("multipart_bytes", multipart_bytes)
....```
Any help is much appreciated.
I’ve tried many of the advises here: Saving video file content from fetch formdata but so far without luck.
I took a look at the open source code for HttpRequest, and there is no helper function for parsing multi-part form data (just the one for parsing JSON data), so I don’t think there’s an Anvil specific way to do this.
That leaves you with Python ways of doing it. There’s a library that’s supposed to parse that data, requests_toolbelt
that is installed in Anvil servers already. I haven’t used it, but it looks like you’d just pass the body to its MultipartDecoder, and it returns you a collection of the parts. No idea if it also converts the images back to binary or not.
Hi.
Thanks! I found a workaround using the requests_toolbelt as suggested. I had to do some sting manupulation in order to find content-type and filename as the embedded header data included som invalid json. Here is the code I ended up using. I’m using this to store images for editor.js in case someone else have a similar usecase
@anvil.server.http_endpoint("/images/store_for_editor_js", methods=["POST"], enable_cors=True, cross_site_session=True)
def upload_image(**params):
id = str(uuid.uuid4())
content_type_string = anvil.server.request.headers['content-type']
file_size = anvil.server.request.headers['content-length']
try:
ip_address = anvil.server.request.headers['x-forwarded-for']
except:
ip_address = "unknown"
content_type = anvil.server.request.body.content_type
multipart_bytes = anvil.server.request.body.get_bytes()
multipart_data = decoder.MultipartDecoder(multipart_bytes, content_type)
for part in multipart_data.parts:
image = part.content # Alternatively, part.text if you want unicode
headers = str(part.headers) #to string since it seems to be invalid json
p1 = headers.find("filename=")
p2 = headers.find("Content-Type")
file_name = headers[p1+10:]
file_name = file_name[0:file_name.find('"')]
content_type_string = headers[p2+17:]
content_type_string = content_type_string[0:content_type_string.find("'")]
my_media = anvil.BlobMedia(content_type=content_type_string, content=image)
row = app_tables.images.add_row(DateTime=datetime.datetime.now(),img=my_media,content_type=content_type_string, size=int(file_size), ip=ip_address, id=id, filename=file_name)
return {
"success" : 1,
"file": {
"url" : "https://yourdomain.anvil.app/_/api/images/"+id
}
}
Perhaps not the most efficient code but it works. 
2 Likes