I've created a Cloud Function on Azure with an HTTP trigger and I want to be able to handle a file upload using a form.
My entry point looks like this:
def main(req: func.HttpRequest) -> func.HttpResponse:
body = req.get_body()
headers=req.headers
...
The problem is that what I get in body
is raw binary and I don't know how to decode it and get the uploaded file.
Does anyone know about a good method to achieve this?
There's, I'd say, an Azure-native way to do that, and there's a Python-native way.
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')
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')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With