Azure Functions 的 Azure Blob 存储输出绑定

通过输出绑定,可在 Azure Functions 中修改和删除 blob 存储数据。

若要了解设置和配置详细信息,请参阅概述

重要

本文使用选项卡来支持多个版本的 Node.js 编程模型。 v4 模型目前处于预览状态,旨在为 JavaScript 和 TypeScript 开发人员提供更为灵活和直观的体验。 在升级指南中详细了解 v3 和 v4 之间的差异。

重要

本文使用选项卡来支持多个版本的 Python 编程模型。 v2 模型已正式发布,旨在提供一种更加以代码为中心的方法,用于通过修饰器创作函数。 有关 v2 模型工作原理的更多详细信息,请参阅 Azure Functions Python 开发人员指南

示例

可以使用以下 C# 模式之一创建 C# 函数:

  • 进程内类库:编译的 C# 函数,该函数在与 Functions 运行时相同的进程中运行。
  • 独立进程类库:编译的 C# 函数,该函数在独立于运行时的进程中运行。 支持在 .NET 5.0 上运行的 C# 函数需要独立的进程。
  • C# 脚本:主要在 Azure 门户中创建 C# 函数时使用。

以下示例是一个 C# 函数,该函数在独立工作进程中运行,并将 blob 触发器与 blob 输入和 blob 输出 blob 绑定配合使用。 在 test-samples-trigger 容器中创建 blob 时,会触发该函数。 它从 test-samples-input 容器读取文本文件,并基于已触发文件的名称在输出容器中创建一个新的文本文件。

using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace SampleApp
{
    public static class BlobFunction
    {
        [Function(nameof(BlobFunction))]
        [BlobOutput("test-samples-output/{name}-output.txt")]
        public static string Run(
            [BlobTrigger("test-samples-trigger/{name}")] string myTriggerItem,
            [BlobInput("test-samples-input/sample1.txt")] string myBlob,
            FunctionContext context)
        {
            var logger = context.GetLogger("BlobFunction");
            logger.LogInformation("Triggered Item = {myTriggerItem}", myTriggerItem);
            logger.LogInformation("Input Item = {myBlob}", myBlob);

            // Blob Output
            return "blob-output content";
        }
    }
}

本部分包含以下示例:

HTTP 触发器,使用 OutputBinding (Java)

下面的示例显示了一个 Java 函数,该函数使用 HttpTrigger 注释来接收包含 blob 存储容器中某个文件的名称的一个参数。 然后,BlobInput 注释读取该文件并将其作为 byte[] 传递给该函数。 BlobOutput 注释绑定到 OutputBinding outputItem,然后,函数使用后者来将输入 blob 的内容写入到所配置的存储容器中。

  @FunctionName("copyBlobHttp")
  @StorageAccount("Storage_Account_Connection_String")
  public HttpResponseMessage copyBlobHttp(
    @HttpTrigger(name = "req", 
      methods = {HttpMethod.GET}, 
      authLevel = AuthorizationLevel.ANONYMOUS) 
    HttpRequestMessage<Optional<String>> request,
    @BlobInput(
      name = "file", 
      dataType = "binary", 
      path = "samples-workitems/{Query.file}") 
    byte[] content,
    @BlobOutput(
      name = "target", 
      path = "myblob/{Query.file}-CopyViaHttp")
    OutputBinding<String> outputItem,
    final ExecutionContext context) {
      // Save blob to outputItem
      outputItem.setValue(new String(content, StandardCharsets.UTF_8));

      // build HTTP response with size of requested blob
      return request.createResponseBuilder(HttpStatus.OK)
        .body("The size of \"" + request.getQueryParameters().get("file") + "\" is: " + content.length + " bytes")
        .build();
  }

队列触发器,使用函数返回值 (Java)

下面的示例显示了一个 Java 函数,该函数使用 QueueTrigger 注释来接收包含 blob 存储容器中某个文件的名称的一个消息。 然后,BlobInput 注释读取该文件并将其作为 byte[] 传递给该函数。 BlobOutput 注释绑定到函数返回值,然后,运行时使用后者来将输入 blob 的内容写入到所配置的存储容器中。

  @FunctionName("copyBlobQueueTrigger")
  @StorageAccount("Storage_Account_Connection_String")
  @BlobOutput(
    name = "target", 
    path = "myblob/{queueTrigger}-Copy")
  public String copyBlobQueue(
    @QueueTrigger(
      name = "filename", 
      dataType = "string",
      queueName = "myqueue-items") 
    String filename,
    @BlobInput(
      name = "file", 
      path = "samples-workitems/{queueTrigger}") 
    String content,
    final ExecutionContext context) {
      context.getLogger().info("The content of \"" + filename + "\" is: " + content);
      return content;
  }

Java 函数运行时库中,对其值将写入 Blob 存储中对象的函数参数使用 @BlobOutput 注释。 参数类型应为 OutputBinding<T>,其中 T 是任何本机 Java 类型或者是一个 POJO。

以下示例显示队列触发的 TypeScript 函数,该函数创建 blob 的副本。 该函数由包含要复制的 Blob 名称的队列消息触发。 新 Blob 名为 {originalblobname}-Copy

import { app, input, InvocationContext, output } from '@azure/functions';

const blobInput = input.storageBlob({
    path: 'samples-workitems/{queueTrigger}',
    connection: 'MyStorageConnectionAppSetting',
});

const blobOutput = output.storageBlob({
    path: 'samples-workitems/{queueTrigger}-Copy',
    connection: 'MyStorageConnectionAppSetting',
});

export async function storageQueueTrigger1(queueItem: unknown, context: InvocationContext): Promise<unknown> {
    return context.extraInputs.get(blobInput);
}

app.storageQueue('storageQueueTrigger1', {
    queueName: 'myqueue-items',
    connection: 'MyStorageConnectionAppSetting',
    extraInputs: [blobInput],
    return: blobOutput,
    handler: storageQueueTrigger1,
});

以下示例显示队列触发的 JavaScript 函数,该函数创建 blob 的副本。 该函数由包含要复制的 Blob 名称的队列消息触发。 新 Blob 名为 {originalblobname}-Copy

const { app, input, output } = require('@azure/functions');

const blobInput = input.storageBlob({
    path: 'samples-workitems/{queueTrigger}',
    connection: 'MyStorageConnectionAppSetting',
});

const blobOutput = output.storageBlob({
    path: 'samples-workitems/{queueTrigger}-Copy',
    connection: 'MyStorageConnectionAppSetting',
});

app.storageQueue('storageQueueTrigger1', {
    queueName: 'myqueue-items',
    connection: 'MyStorageConnectionAppSetting',
    extraInputs: [blobInput],
    return: blobOutput,
    handler: (queueItem, context) => {
        return context.extraInputs.get(blobInput);
    },
});

下面的示例演示如何创建传入 Blob 的副本作为 PowerShell 函数的输出。

在函数的配置文件 (function.json) 中,trigger 元数据属性用于指定 path 属性中的输出 Blob 名称。

注意

为了避免无限循环,请确保输入和输出路径不同。

{
  "bindings": [
    {
      "name": "myInputBlob",
      "path": "data/{trigger}",
      "connection": "MyStorageConnectionAppSetting",
      "direction": "in",
      "type": "blobTrigger"
    },
    {
      "name": "myOutputBlob",
      "type": "blob",
      "path": "data/copy/{trigger}",
      "connection": "MyStorageConnectionAppSetting",
      "direction": "out"
    }
  ],
  "disabled": false
}

下面是 PowerShell 代码:

# Input bindings are passed in via param block.
param([byte[]] $myInputBlob, $TriggerMetadata)
Write-Host "PowerShell Blob trigger function Processed blob Name: $($TriggerMetadata.Name)"
Push-OutputBinding -Name myOutputBlob -Value $myInputBlob

以下示例显示了 Blob 输入和输出绑定。 该示例取决于使用的是 v1 还是 v2 Python 编程模型

代码可创建 Blob 的副本。

import logging
import azure.functions as func

app = func.FunctionApp()

@app.function_name(name="BlobOutput1")
@app.route(route="file")
@app.blob_input(arg_name="inputblob",
                path="sample-workitems/test.txt",
                connection="<BLOB_CONNECTION_SETTING>")
@app.blob_output(arg_name="outputblob",
                path="newblob/test.txt",
                connection="<BLOB_CONNECTION_SETTING>")
def main(req: func.HttpRequest, inputblob: str, outputblob: func.Out[str]):
    logging.info(f'Python Queue trigger function processed {len(inputblob)} bytes')
    outputblob.set(inputblob)
    return "ok"

特性

进程内独立工作进程 C# 库都使用特性来定义函数。 C# 脚本改用 function.json 配置文件,如 C# 脚本指南中所述。

BlobOutputAttribute 构造函数采用以下参数:

参数 说明
BlobPath Blob 的路径。
Connection 指定如何连接到 Azure Blob 的应用设置或设置集合的名称。 请参阅连接

在本地开发时,需要将应用程序设置添加到 Values 集合中的 local.settings.json 文件中。

修饰符

仅适用于 Python v2 编程模型。

对于使用修饰器定义的 Python v2 函数,blob_inputblob_output 上的以下属性可定义 Blob 存储触发器:

properties 说明
arg_name 表示函数代码中的 Blob 的变量的名称。
path Blob 的路径。对于 blob_input 修饰器,它是 blob 读取。 对于 blob_output 修饰器,它是输入 Blob 的输出或副本。
connection 存储帐户连接字符串。
dataType 对于动态类型化语言,指定基础数据类型。 可能的值为 stringbinarystream。 有关更多详细信息,请参阅触发器和绑定概念

对于使用 function.json 定义的 Python 函数,请参阅“配置”部分。

批注

使用 @BlobOutput 特性可以访问触发函数的 blob。 如果将字节数组与特性一起使用,请将 dataType 设置为 binary。 有关详细信息,请参阅输出示例

配置

仅适用于 Python v1 编程模型

下表说明了可以在传递给 output.storageBlob() 方法的 options 对象上设置的属性。

properties 说明
路径 Blob 容器的路径。
连接 指定如何连接到 Azure Blob 的应用设置或设置集合的名称。 请参阅连接

下表解释了在 function.json 文件中设置的绑定配置属性。

属性 说明
type 必须设置为 blob
direction 对于输出绑定,必须设置为 out用法部分中已阐述异常。
name 表示函数代码中的 Blob 的变量的名称。 设置为 $return 可引用函数返回值。
路径 Blob 容器的路径。
连接 指定如何连接到 Azure Blob 的应用设置或设置集合的名称。 请参阅连接

有关完整示例的信息,请参阅示例部分

使用情况

Blob 输出支持的绑定类型取决于函数应用中使用的扩展包版本和 C# 形式。

如果希望函数写入单个 blob,blob 输出绑定可以绑定到以下类型:

类型 说明
string Blob 内容即字符串。 在 blob 内容为简单文本时使用。
byte[] Blob 内容的字节数。
JSON 可序列化类型 表示 JSON blob 内容的对象。 函数尝试将普通的旧 CLR 对象 (POCO) 类型序列化为 JSON 数据。

如果希望函数写入多个 blob,blob 输出绑定可以绑定到以下类型:

类型 说明
T[],其中 T 是单 blob 输出绑定类型之一 包含多个 blob 的内容的数组。 每个条目表示一个 blob 的内容。

对于其他输出方案,请直接创建和使用 Azure.Storage.Blobs 中的类型。

仅当 blob 大小较小时才建议绑定到 stringByte[]。 我们推荐此做法,因为整个 blob 内容都会加载到内存中。 对于大多数 blob,请使用 StreamBlobClient 类型。 有关详细信息,请参阅并发和内存使用情况

如果尝试绑定到某个存储 SDK 类型时收到了错误消息,请确保已引用正确的存储 SDK 版本

还可使用 StorageAccountAttribute 指定要使用的存储帐户。 如果需要使用一个不同于库中的其他函数的存储帐户,可以执行此操作。 构造函数采用包含存储连接字符串的应用设置的名称。 可以在参数、方法或类级别应用该特性。 以下示例演示类级别和方法级别:

[StorageAccount("ClassLevelStorageAppSetting")]
public static class AzureFunctions
{
    [FunctionName("BlobTrigger")]
    [StorageAccount("FunctionLevelStorageAppSetting")]
    public static void Run( //...
{
    ....
}

要使用的存储帐户按以下顺序确定:

  • BlobTrigger 特性的 Connection 属性。
  • 作为 BlobTrigger 特性应用到同一参数的 StorageAccount 特性。
  • 应用到函数的 StorageAccount 特性。
  • 应用到类的 StorageAccount 特性。
  • 函数应用的默认存储帐户,在 AzureWebJobsStorage 应用程序设置中定义。

使用 @BlobOutput 特性可以访问触发函数的 blob。 如果将字节数组与特性一起使用,请将 dataType 设置为 binary。 有关详细信息,请参阅输出示例

通过直接返回值或使用 context.extraOutputs.set() 来访问 blob 数据。

通过与 function.json 文件中绑定名称参数指定的名称匹配的字符串参数访问 Blob 数据。

可以将函数参数声明为以下类型以写出到 Blob 存储:

  • 字符串为 func.Out[str]
  • 流为 func.Out[func.InputStream]

有关详细信息,请参阅输出示例

连接

connection 属性是对环境配置的引用,它指定应用应如何连接到 Azure Blob。 它可能指定:

如果配置的值既是单个设置的完全匹配,也是其他设置的前缀匹配,则使用完全匹配。

连接字符串

要获取连接字符串,请执行管理存储帐户访问密钥中显示的步骤。 连接字符串必须属于某个常规用途存储帐户,而不能属于Blob 存储帐户

此连接字符串应存储在应用程序设置中,其名称与绑定配置的 connection 属性指定的值匹配。

如果应用设置名称以“AzureWebJobs”开始,则只能在此处指定该名称的余下部分。 例如,如果将 connection 设置为“MyStorage”,则 Functions 运行时会查找名为“AzureWebJobsMyStorage”的应用设置。 如果将 connection 留空,函数运行时将使用名为 AzureWebJobsStorage 的应用设置中的默认存储连接字符串。

基于标识的连接

如果使用 5.x 或更高版本的扩展,则无需将连接字符串与机密配合使用,可以让应用使用 Azure Active Directory 标识。 要使用标识,需要定义公共前缀下的设置,该前缀映射到触发器和绑定配置中的 connection 属性。

如果将 connection 设置为“AzureWebJobsStorage”,请参阅使用标识连接到主机存储。 对于所有其他连接,扩展需要以下属性:

属性 环境变量模板 说明 示例值
Blob 服务 URI <CONNECTION_NAME_PREFIX>__serviceUri1 要连接到的 Blob 服务的数据平面 URI,使用 HTTPS 方案。 https://<storage_account_name>.blob.core.chinacloudapi.cn

1 <CONNECTION_NAME_PREFIX>__blobServiceUri 可以用作别名。 如果连接配置将由 Blob 触发器使用,则 blobServiceUri 还必须带有 queueServiceUri。 请参阅下文。

如果要跨 Blob、队列和/或表使用总体连接配置,则无法使用 serviceUri 窗体。 URI 只能指定 Blob 服务。 作为一种替代方案,可以为每个服务专门提供一个 URI,从而允许使用单个连接。 如果这两个版本均提供,则将使用多服务窗体。 若要为多个服务(而不是 <CONNECTION_NAME_PREFIX>__serviceUri)配置连接,请设置:

属性 环境变量模板 说明 示例值
Blob 服务 URI <CONNECTION_NAME_PREFIX>__blobServiceUri 要连接到的 Blob 服务的数据平面 URI,使用 HTTPS 方案。 https://<storage_account_name>.blob.core.chinacloudapi.cn
队列服务 URI(Blob 触发器需要2 <CONNECTION_NAME_PREFIX>__queueServiceUri 使用 HTTPS 方案的队列服务的数据平面 URI。 仅 Blob 触发器需要该值。 https://<storage_account_name>.queue.core.chinacloudapi.cn

2 Blob 触发器通过将有害 blob 写入队列来处理多次重试中出现的故障。 在 serviceUri 窗体中,将使用 AzureWebJobsStorage 连接。 但是,在指定 blobServiceUri 时,还必须为 queueServiceUri 提供队列服务 URI。 建议使用与 Blob 服务相同的存储帐户中的服务。 还需要通过分配存储队列数据参与者等角色来确保触发器可以在配置的队列服务中读取和写入消息。

可以设置其他属性来自定义连接。 请参阅基于标识的连接的通用属性

在 Azure Functions 服务中托管时,基于标识的连接将使用托管标识。 默认情况下使用系统分配的标识,但可以使用 credentialclientID 属性来指定用户分配的标识。 请注意,不支持为用户分配的标识配置资源 ID。 在其他上下文(如本地开发)中运行时,将改用开发人员标识,尽管可以进行自定义。 请参阅使用基于标识的连接进行本地开发

向标识授予权限

无论使用何种标识,都必须具有执行所需操作的权限。 需要使用内置角色或者提供这些权限的自定义角色在 Azure RBAC 中分配角色

重要

某些权限可能由并非所有上下文都需要的目标服务公开。 尽可能遵循最低权限原则,仅授予标识所需的权限。 例如,如果应用只需要从数据源进行读取即可,则使用仅具有读取权限的角色。 分配一个也具有该服务写入权限的角色并不恰当,因为对于读取操作来说,写入是多余的权限。 同样,你也希望确保角色分配的范围仅限于需要读取的资源。

你需要创建一个角色分配,以在运行时提供对 Blob 容器的访问权限。 所有者等管理角色还不够。 下表显示了在正常操作中使用 Blob 存储扩展时建议使用的内置角色。 根据所编写的代码,应用程序可能需要进一步的权限。

绑定类型 内置角色示例
触发器 存储 Blob 数据所有者存储队列数据参与者1

此外,还必须向 AzureWebJobsStorage 连接授予额外的权限。2
输入绑定 存储 Blob 数据读者
输出绑定 存储 Blob 数据所有者

1 Blob 触发器通过将有害 blob 写入到连接所指定的存储帐户上的队列来处理多次重试中出现的故障。

2 AzureWebJobsStorage 连接在内部用于启用触发器的 blob 和队列。 如果将其配置为使用基于标识的连接,则该连接将需要超出默认要求的其他权限。 存储 Blob 数据所有者存储队列数据参与者存储帐户参与者角色涵盖了所需的权限。 若要了解详细信息,请参阅使用标识连接到主机存储

异常和返回代码

绑定 参考
Blob Blob 错误代码
Blob、表、队列 存储错误代码
Blob、表、队列 故障排除

后续步骤