你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

JavaScript 教程:使用 Azure Functions 和 Blob 存储上传和分析文件

在本教程中,你将了解如何将图像上传到 Azure Blob 存储并使用 Azure Functions、计算机视觉和 Cosmos DB 对其进行处理。 你还将了解如何在此过程中实现 Azure Function 触发器和绑定。 这些服务将一起分析包含文本的上传图像,从中提取文本,然后将文本存储在数据库行中,供以后分析或用于其他用途。

Azure Blob 存储是 Microsoft 针对云提供的可大规模缩放的对象存储解决方案。 Blob 存储设计用于存储图像和文档、流式处理媒体文件、管理备份和存档数据等等。 有关 Blob 存储的详细信息,请阅读概述页面

警告

本教程旨在快速采用,因此它不遵循默认安全要求。 若要详细了解此方案并采用默认安全目标,请前往安全注意事项

Azure Cosmos DB 是一种用于新式应用开发的完全托管的 NoSQL 数据库和关系数据库。

Azure Functions 是一种无服务器计算机解决方案,允许你编写和运行小块代码作为高度可缩放、无服务器、事件驱动的函数。 有关 Azure Functions 的详细信息,请阅读概述页面

本教程介绍如何执行下列操作:

  • 将图像和文件上传到 Blob 存储
  • 使用 Azure Function 事件触发器处理上传到 Blob 存储的数据
  • 使用 Azure AI 服务分析图像
  • 使用 Azure Function 输出绑定将数据写入 Cosmos DB

该体系结构图显示了以下过程:将图像 blob 添加到 Blob 存储中,然后由 Azure 函数进行分析,并将分析结果插入 Cosmos DB 中。

先决条件

创建存储帐户和容器

第一步是创建存储帐户来保存上传的 blob 数据,在本方案中为包含文本的图像。 存储帐户可提供多种不同的服务,但本教程仅使用了 Blob 存储。

  1. 在 Visual Studio Code 中,选择 Ctrl + Shift + P 以打开命令面板。

  2. 搜索“Azure 存储: 创建存储帐户(高级)”。

  3. 使用下表创建存储资源。

    设置
    名称 输入“msdocsstoragefunction”或类似内容。
    资源组 创建你之前创建的 msdocs-storage-function 资源组。
    静态 Web 托管 否。
  4. 在 Visual Studio Code 中,选择 Shift + Alt + A 以打开 Azure 资源管理器。

  5. 展开“存储”部分,展开订阅节点并等待创建资源。

在 Visual Studio Code 中创建容器

  1. 仍在 Azure 资源浏览器中,找到新的存储资源,展开资源以查看节点。
  2. 右键单击“Blob 容器”,然后选择“创建 Blob 容器”。
  3. 输入名称 images。 这将创建一个专用容器。

在 Azure 门户中从专用容器更改为公共容器

此过程需要一个公共容器。 若要更改该配置,请在 Azure 门户中进行更改。

  1. 右键单击 Azure 资源管理器中的存储资源,选择“在门户中打开”。
  2. 在“数据存储”部分,选择“容器”。
  3. 找到容器 images,然后选择行尾的 ...(省略号)。
  4. 选择“更改访问级别”。
  5. 选择“Blob (仅匿名读取访问 blob)”,然后选择“确定”。
  6. 返回到 Visual Studio Code。

在 Visual Studio Code 中检索连接字符串

  1. 在 Visual Studio Code 中,选择 Shift + Alt + A 以打开 Azure 资源管理器。
  2. 右键单击存储资源,然后选择“复制连接字符串”。
  3. 将该内容粘贴到某处供稍后使用。
  4. 记下存储帐户的名称 msdocsstoragefunction 供稍后使用。

创建 Azure AI 视觉服务

接下来,创建 Azure AI 视觉服务帐户来处理上传的文件。 视觉是 Azure AI 服务的一部分,可提供各种用于从图像中提取数据的功能。 有关 Azure AI 视觉的详细信息,请阅读概述页面

  1. 在门户顶部的搜索栏中,搜索“计算机”并选择标有“计算机视觉”的结果。

  2. 在“计算机视觉”页面上,选择“+ 创建”。

  3. 在“创建计算机视觉”页面上,输入以下值:

    • 订阅:选择所需的订阅。
    • 资源组:使用之前创建的 msdocs-storage-function 资源组。
    • 区域:选择离你最近的区域。
    • 名称:输入名称 msdocscomputervision
    • 定价层:如果可用,请选择“免费”,否则选择“标准 S1”。
    • 选中“负责任的 AI 声明”框(如果同意这些条款)

    显示如何创建新的计算机视觉服务的屏幕截图。

  4. 选择底部的“查看 + 创建”。 Azure 需要花点时间来验证你输入的信息。 验证设置后,选择“创建”,Azure 将开始预配计算机视觉服务,这可能需要一些时间。

  5. 操作完成后,选择“转到资源”。

检索计算机视觉键

接下来,我们需要找到计算机视觉服务的密钥和终结点 URL,以在 Azure Function App 中使用。

  1. 在“计算机视觉”概述页面上,选择“密钥和终结点”。

  2. 在“键和终结点”页面上,复制“键 1”的值和“终结点”的值,并将它们粘贴到某处供稍后使用。 终结点的格式应为 https://YOUR-RESOURCE-NAME.cognitiveservices.azure.com/

显示如何检索计算机视觉服务的密钥和 URL 终结点的屏幕截图。

创建 Cosmos DB 服务帐户

创建 Cosmos DB 服务帐户来存储文件分析结果。 Azure Cosmos DB 是一种用于新式应用开发的完全托管的 NoSQL 数据库和关系数据库。 你可以详细了解 Cosmos DB 及其针对多个不同行业数据库的支持 API

虽然本教程在创建资源时指定了一个 API,但 Cosmos DB 的 Azure 函数绑定的配置方式与所有 Cosmos DB API 相同。

  1. 在门户顶部的搜索栏中,搜索“Azure Cosmos DB”并选择相应结果。

  2. 在“Azure Cosmos DB”页面上,选择“+ 创建”。 从 API 选项列表中选择“Azure Cosmos DB for NoSQL”。

  3. 在“创建 Cosmos DB”页面上,输入以下值:

    • 订阅:选择所需的订阅。
    • 资源组:使用之前创建的 msdocs-storage-function 资源组。
    • 区域:选择你的资源组所在的同一区域。
    • 名称:输入名称 msdocscosmosdb
    • 定价层:如果可用,请选择“免费”,否则选择“标准 S1”。
  4. 选择底部的“查看 + 创建”。 Azure 会花点时间验证输入的信息。 验证设置后,选择“创建”,Azure 将开始预配计算机视觉服务,这可能需要一些时间。

  5. 操作完成后,选择“转到资源”。

  6. 依次选择“数据资源管理器”和“新建容器”。

  7. 使用以下设置创建新数据库和容器:

    • 创建新数据库 ID:StorageTutorial
    • 输入新的容器 ID:analysis
    • 输入分区键:/type
  8. 保留其余默认设置,并选择“确定”。

获取 Cosmos DB 连接字符串

获取 Cosmos DB 服务帐户的连接字符串,以便在 Azure 函数应用中使用。

  1. 在“Cosmos DB”概述页面上,选择“键”。

  2. 在“键”页面上,复制主连接字符串以供稍后使用。

下载并配置示例项目

本教程中使用的 Azure 函数的代码可在此 GitHub 存储库JavaScript-v4 子目录中找到。 也可以使用以下命令克隆项目。

git clone https://github.com/Azure-Samples/msdocs-storage-bind-function-service.git \
cd msdocs-storage-bind-function-service/javascript-v4 \
code .

示例项目将完成以下任务:

  • 检索环境变量以连接到存储帐户、计算机视觉和 Cosmos DB 服务
  • 接受上传的文件作为 blob 参数
  • 使用计算机视觉服务分析 blob
  • 使用输出绑定将分析的图像文本作为 JSON 对象插入 Cosmos DB 中

下载并打开项目后,需要了解一些基本概念:

概念 目的
函数 Azure 函数由函数代码和绑定定义。 它们位于 ./src/functions/process-blobs.js 中。
触发器和绑定 触发器和绑定指示预期传入或传出函数的数据,以及要发送或接收该数据的服务。

本教程中使用的触发器和绑定无需编写代码即可连接到服务,可加快开发过程。

存储 Blob 输入触发器

下面的代码指定了在有 Blob 上传到图像容器时触发相应的函数。 该函数可在任何 Blob 名称(包括分层文件夹)上触发。


// ...preceding code removed for brevity

app.storageBlob('process-blob-image', { 
    path: 'images/{name}',                // Storage container name: images, Blob name: {name}
    connection: 'StorageConnection',      // Storage account connection string
    handler: async (blob, context) => {

// ... function code removed for brevity
  • app.storageBlob - 存储 Blob 输入触发器用于将函数绑定到 Blob 存储中的上传事件。 触发器具有两个必需参数:
    • path:触发器监视事件的路径。 该路径包括容器名称 images 以及 blob 名称的变量替换。 此 blob 名称是从 name 属性中检索的。
    • {name}:已上传的 blob 的名称。 blob 的使用是传入函数的 blob 的参数名称。 请勿更改值 blob
    • connection:存储帐户的连接字符串。 在本地开发时,值 StorageConnectionlocal.settings.json 文件中的名称相匹配。

Cosmos DB 输出触发器

完成相应函数后,该函数会将返回的对象作为数据插入到 Cosmos DB 中。


// ... function definition object
app.storageBlob('process-blob-image', { 
    
        // removed for brevity    
        
        // Data to insert into Cosmos DB
        const id = uuidv4().toString();
        const analysis = await analyzeImage(blobUrl);
        
        // `type` is the partition key 
        const dataToInsertToDatabase = {
                id,
                type: 'image',
                blobUrl,
                blobSize: blob.length,
                analysis,
                trigger: context.triggerMetadata
            }

        return dataToInsertToDatabase;
    }),

    // Output binding for Cosmos DB
    return: output.cosmosDB({
        connection: 'CosmosDBConnection',
        databaseName:'StorageTutorial',
        containerName:'analysis'
    })
});

对于本文中的容器,以下是必需的属性:

  • id:Cosmos DB 创建新行所需的 ID。

  • /type:创建容器时指定的分区键。

  • output.cosmosDB - Cosmos DB 输出触发器用于将函数的结果插入到 Cosmos DB 中。

    • connection:存储帐户的连接字符串。 值 StorageConnectionlocal.settings.json 文件中的名称匹配。
    • databaseName:要连接到的 Cosmos DB 数据库。
    • containerName:写入函数返回的已分析图像文本值的表的名称。 该表必须已存在。

Azure 函数代码

下面是完整的函数代码。

const { app, input, output } = require('@azure/functions');
const { v4: uuidv4 } = require('uuid');
const { ApiKeyCredentials } = require('@azure/ms-rest-js');
const { ComputerVisionClient } = require('@azure/cognitiveservices-computervision');
const sleep = require('util').promisify(setTimeout);

const STATUS_SUCCEEDED = "succeeded";
const STATUS_FAILED = "failed"

const imageExtensions = ["jpg", "jpeg", "png", "bmp", "gif", "tiff"];

async function analyzeImage(url) {

    try {

        const computerVision_ResourceKey = process.env.ComputerVisionKey;
        const computerVision_Endpoint = process.env.ComputerVisionEndPoint;

        const computerVisionClient = new ComputerVisionClient(
            new ApiKeyCredentials({ inHeader: { 'Ocp-Apim-Subscription-Key': computerVision_ResourceKey } }), computerVision_Endpoint);

        const contents = await computerVisionClient.analyzeImage(url, {
            visualFeatures: ['ImageType', 'Categories', 'Tags', 'Description', 'Objects', 'Adult', 'Faces']
        });

        return contents;

    } catch (err) {
        console.log(err);
    }
}
app.storageBlob('process-blob-image', { 
    path: 'images/{name}',
    connection: 'StorageConnection',
    handler: async (blob, context) => {

        context.log(`Storage blob 'process-blob-image' url:${context.triggerMetadata.uri}, size:${blob.length} bytes`);

        const blobUrl = context.triggerMetadata.uri;
        const extension = blobUrl.split('.').pop();

        if(!blobUrl) {
            // url is empty
            return;
        } else if (!extension || !imageExtensions.includes(extension.toLowerCase())){
            // not processing file because it isn't a valid and accepted image extension
            return;
        } else {
            //url is image
            const id = uuidv4().toString();
            const analysis = await analyzeImage(blobUrl);
            
            // `type` is the partition key 
            const dataToInsertToDatabase = {
                    id,
                    type: 'image',
                    blobUrl,
                    blobSize: blob.length,
                    ...analysis,
                    trigger: context.triggerMetadata
                }

            return dataToInsertToDatabase;
        }

        
    },
    return: output.cosmosDB({
        connection: 'CosmosDBConnection',
        databaseName:'StorageTutorial',
        containerName:'analysis'
    })
});

此代码还从环境变量中检索基本配置值,例如 Blob 存储连接字符串和计算机视觉密钥。 这些环境变量在部署后添加到 Azure 函数环境。

默认函数还会使用名为 AnalyzeImage 的第二种方法。 此代码使用计算机视觉帐户的 URL 终结点和密钥向计算机视觉发出处理图像的请求。 该请求返回图像中发现的所有文本。 此文本将使用出站绑定写入 Cosmos DB。

配置本地设置

若要在本地运行项目,请在 ./local.settings.json 文件中输入环境变量。 使用之前在创建 Azure 资源时保存的值填充占位符值。

尽管 Azure 函数代码在本地运行,但它将连接到基于云的存储服务,而不是使用任何本地模拟器。

{
  "IsEncrypted": false,
  "Values": {
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "AzureWebJobsStorage": "",
    "StorageConnection": "STORAGE-CONNECTION-STRING",
    "StorageAccountName": "STORAGE-ACCOUNT-NAME",
    "StorageContainerName": "STORAGE-CONTAINER-NAME",
    "ComputerVisionKey": "COMPUTER-VISION-KEY",
    "ComputerVisionEndPoint":  "COMPUTER-VISION-ENDPOINT",
    "CosmosDBConnection": "COSMOS-DB-CONNECTION-STRING"
  }
}

创建 Azure Functions 应用

现在可以使用 Visual Studio Code 扩展将应用程序部署到 Azure。

  1. 在 Visual Studio Code 中,选择 Shift + Alt + A 以打开 Azure 资源管理器。

  2. 在“Functions”部分,找到并右键单击订阅,然后选择“在 Azure 中创建函数应用(高级)”。

  3. 使用下表创建函数资源。

    设置
    名称 输入 msdocsprocessimage 或类似内容。
    运行时堆栈 选择一个 Node.js LTS 版本。
    编程模型 选择“v4”。
    OS 选择“Linux”。
    资源组 选择之前创建的 msdocs-storage-function 资源组。
    位置 选择你的资源组所在的同一区域。
    计划类型 选择“消耗”。
    Azure 存储 选择前面创建的存储帐户。
    Application Insights 暂时跳过。
  4. Azure 将预配请求的资源,这需要一些时间才能完成。

部署 Azure Functions 应用

  1. 上一个资源创建过程完成后,在 Azure 资源管理器的“Functions”部分中右键单击新资源,然后选择“部署到函数应用”。
  2. 如果系统询问“是否确定要部署...”,请选择“部署”。
  3. 该过程完成后,会显示一条通知,其中包含“上传设置”的选项。 选择该选项。 这会将值从 local.settings.json 文件复制到 Azure 函数应用中。 如果通知在你进行选择之前就消失了,请继续执行下一部分。

为存储和计算机视觉添加应用设置

如果在通知中选择了“上传设置”,请跳过此部分。

Azure 函数已成功部署,但它还无法连接到存储帐户和计算机视觉服务。 必须先将正确的密钥和连接字符串添加到 Azure Functions 应用的配置设置。

  1. 在 Azure 资源管理器的“Functions”部分,查找你的资源,右键单击“应用程序设置”,然后选择“添加新设置”。

  2. 为以下机密输入新的应用设置。 将机密值从本地项目复制并粘贴到 local.settings.json 文件中。

    设置
    StorageConnection
    StorageAccountName
    StorageContainerName
    ComputerVisionKey
    ComputerVisionEndPoint
    CosmosDBConnection

将 Azure Function 连接到不同服务所需的所有环境变量现已到位。

将图像上传到 Blob 存储

现在可以测试应用程序了! 你可以将 blob 上传到容器,然后验证图像中的文本是否已保存到 Cosmos DB。

  1. 在 Visual Studio Code 的 Azure 资源管理器中,在“存储”部分查找并展开你的存储资源。
  2. 展开“Blob 容器”,右键单击容器名称 images,然后选择“上传文件”。
  3. 你可以在可下载示例项目的根目录的 images 文件夹中找到一些示例图像,也可以使用自己的图像。
  4. 对于“目标目录”,接受默认值 /
  5. 等待文件上传并列在容器中。

查看图片的文本分析

接下来,可以验证上传是否触发了 Azure 函数,以及图像中的文本是否已正确分析并保存到 Cosmos DB 中。

  1. 在 Visual Studio Code 中,在 Azure 资源管理器的 Azure Cosmos DB 节点下,选择资源,然后展开该资源以查找数据库“StorageTutorial”。

  2. 展开数据库节点。

  3. 分析容器现已应该可用。 选择容器的“文档”节点,以预览其中的数据。 你应会看到已上传文件的已处理图像文本的条目。

    {
        "id": "3cf7d6f0-a362-421e-9482-3020d7d1e689",
        "type": "image",
        "blobUrl": "https://msdocsstoragefunction.blob.core.windows.net/images/presentation.png",
        "blobSize": 1383614,
        "analysis": {  ... details removed for brevity ...
            "categories": [],
            "adult": {},
            "imageType": {},
            "tags": [],
            "description": {},
            "faces": [],
            "objects": [],
            "requestId": "eead3d60-9905-499c-99c5-23d084d9cac2",
            "metadata": {},
            "modelVersion": "2021-05-01"
        },
        "trigger": { 
            "blobTrigger": "images/presentation.png",
            "uri": "https://msdocsstorageaccount.blob.core.windows.net/images/presentation.png",
            "properties": {
                "lastModified": "2023-07-07T15:32:38+00:00",
                "createdOn": "2023-07-07T15:32:38+00:00",
                "metadata": {},
                ... removed for brevity ...
                "contentLength": 1383614,
                "contentType": "image/png",
                "accessTier": "Hot",
                "accessTierInferred": true,
            },
            "metadata": {},
            "name": "presentation.png"
        },
        "_rid": "YN1FAKcZojEFAAAAAAAAAA==",
        "_self": "dbs/YN1FAA==/colls/YN1FAKcZojE=/docs/YN1FAKcZojEFAAAAAAAAAA==/",
        "_etag": "\"7d00f2d3-0000-0700-0000-64a830210000\"",
        "_attachments": "attachments/",
        "_ts": 1688743969
    }
    

恭喜! 你已成功使用 Azure Functions 和计算机视觉处理上传到 Blob 存储的图像。

疑难解答

请使用下表帮助解决此过程中的问题。

问题 解决方法
await computerVisionClient.read(url); 出现错误 Only absolute URLs are supported 请确保 ComputerVisionEndPoint 终结点采用 https://YOUR-RESOURCE-NAME.cognitiveservices.azure.com/ 格式。

安全注意事项

作为初学者教程,此解决方案没有演示默认情况下的安全做法。 这是为了使你能够成功部署解决方案。 成功部署后的下一步是保护资源。 此解决方案使用三个 Azure 服务,每个服务都有自己的安全功能和安全默认配置注意事项:

代码示例

清理资源

如果不打算继续使用此应用程序,可以通过删除资源组来删除创建的资源。

  1. 从 Azure 资源管理器中选择“资源组”
  2. 从列表中找到并右键单击 msdocs-storage-function 资源组。
  3. 选择“删除”。 删除资源组的过程可能需要几分钟才能完成。