使用 Python 上传 Azure Cloud Functions HTTP 文件

mar*_*lli 4 azure python-3.x azure-functions serverless

我在 Azure 上创建了一个带有 HTTP 触发器的云函数,我希望能够使用表单处理文件上传。
我的入口点是这样的:

def main(req: func.HttpRequest) -> func.HttpResponse:
    body = req.get_body()
    headers=req.headers
    ...
Run Code Online (Sandbox Code Playgroud)

问题是我得到的body是原始二进制文件,我不知道如何解码它并获取上传的文件。

有谁知道实现这一目标的好方法?

xim*_*ica 6

我想说,有一种 Azure 本地方式可以做到这一点,还有一种 Python 本地方式。

Azure 本地方法

azure.functions.HttpRequest有一个files属性是MultiDict生成器。以下是如何使用它:

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')
Run Code Online (Sandbox Code Playgroud)

Python 原生方法

如果您只想使用标准的 Python 库(例如为了更多的可移植性),那么您应该知道您需要在正文中解析的是MIME 多部分消息,以二进制形式:

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--
Run Code Online (Sandbox Code Playgroud)

要重现的示例脚本:

echo 'this is a test document' > test.txt && curl -F 'data=@test.txt' 'https://[..yourfunctionname..].azurewebsites.net/api/HttpTrigger'
Run Code Online (Sandbox Code Playgroud)

您会感到惊讶,但该email模块为您做到这一点(我个人认为它的名字很糟糕)。

下面的示例代码(注意:没有错误处理!)以突出核心思想:

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')
Run Code Online (Sandbox Code Playgroud)