There's, I'd say, an Azure-native way to do that, and there's a Python-native way.
Azure-native method
azure.functions.HttpRequest has a files property which is a MultiDict generator. Here's how to use that:
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
for input_file in req.files.values():
filename = input_file.filename
contents = input_file.stream.read()
logging.info('Filename: %s' % filename)
logging.info('Contents:')
logging.info(contents)
[..process the file here as you want..]
return func.HttpResponse(f'Done\n')
Python-native method
If you want to do it only with the standard Python library (e.g. for more portability), then you should know that what you need to parse in the body is the MIME multipart message, in a binary form:
Content-Type: multipart/form-data; boundary=--------------------------1715cbf149d89cd9
--------------------------1715cbf149d89cd9
Content-Disposition: form-data; name="data"; filename="test.txt"
Content-Type: application/octet-stream
this is a test document
--------------------------1715cbf149d89cd9--
Sample script to reproduce:
echo 'this is a test document' > test.txt && curl -F '[email protected]' 'https://[..yourfunctionname..].azurewebsites.net/api/HttpTrigger'
You will be surprised but the email module would do it for you (I personally think it's poorly named).
Sample code below (note: with no error handling!) to highlight the core idea:
import cgi
import email
import logging
import azure.functions as func
def main(req: func.HttpRequest) -> func.HttpResponse:
# Content-Type should be 'multipart/form-data' with some boundary
content_type = req.headers.get('Content-Type')
body = req.get_body().decode('utf-8')
mime_template = 'MIME-Version: 1.0\nContent-Type: %s\n%s'
post_data = email.parser.Parser().parsestr(mime_template % (content_type, body))
# Content-Disposition header format:
# 'form-data; name="data"; filename="test.txt"'
disposition = post_data.get_payload()[0]['Content-Disposition']
disposition_value = cgi.parse_header('Content-Disposition: %s' % disposition)[1]
filename = disposition_value['filename']
contents = post_data.get_payload()[0].get_payload()
logging.info('Filename: %s' % filename)
logging.info('Contents:')
logging.info(contents)
[..process the file here as you want..]
return func.HttpResponse(f'Done\n')