首页 > 解决方案 > 如何重构代码以将 HTTP 触发器转换为 Blob 触发器?

问题描述

我有这个功能,基本上可以将视频上传到 YouTube。但是,目前我必须对视频所在的实际文件路径进行硬编码(例如:@"C:\Users\Peter\Desktop\audio\test.mp4";)。我想知道有没有办法让这个更有活力。例如,现在我正在使用 HTTP 触发器,但是如何重构代码以使其成为 Blob 触发器?因此,当我将新的 .mp4 文件上传到我的 blob 存储容器中时,会触发此功能。

我问这个是因为,我计划将此功能移动到 Azure 门户,在那里,我将无法像现在那样指定本地文件路径。提前致谢。

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Upload;
using Google.Apis.YouTube.v3.Data;
using System.Reflection;
using Google.Apis.YouTube.v3;
using Google.Apis.Services;
using System.Threading;

namespace UploadVideoBlob
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task Run([BlobTrigger("video/{name}")]Stream myBlob, string name, Microsoft.Azure.WebJobs.ExecutionContext context, ILogger log)
        {
            UserCredential credential;
            using(var stream = new FileStream(System.IO.Path.Combine(context.FunctionDirectory, "client_secrets.json"), FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { YouTubeService.Scope.YoutubeUpload },
                    "user",
                    CancellationToken.None
                );
            }

            var youtubeService = new YouTubeService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
            });

            var video = new Video();
            video.Snippet = new VideoSnippet();
            video.Snippet.Title = "Default Video Title";
            video.Snippet.Description = "Default Video Description";
            video.Snippet.Tags = new string[] { "tag1", "tag2" };
            video.Snippet.CategoryId = "22";
            video.Status = new VideoStatus();
            video.Status.PrivacyStatus = "unlisted";
            var VideoInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", myBlob, "video/*");
            await VideoInsertRequest.UploadAsync();
        }
    }
}

函数.json

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.29",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "blobTrigger",
      "path": "video/{name}",
      "name": "myBlob"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/UploadVideoBlob.dll",
  "entryPoint": "UploadVideoBlob.Function1.Run"
}

client_secrets.json

{
  "installed": {
    "client_id": "147300761218-dl0rhktkoj8arh0ebu5pu56es06hje5p.apps.googleusercontent.com",
    "project_id": "mytestproj",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "xxxxxxxxxxxxxxxxxx",
    "redirect_uris": [ "urn:ietf:wg:oauth:2.0:oob"]
  }
}

标签: c#.net-coreazure-functionsazure-blob-storage

解决方案


我有一些空闲时间,尽管使用 Azure Functions 已经一年多了,但从未有机会实现实际的BlobTrigger. 所以我想我会分享我的小核心 3.0 测试实现,并添加一个矫枉过正的答案作为@Bryan Lewis 正确答案的补充。

如果你想在 Azure 上启动之前对此进行测试,你应该首先确保你有 Azure 存储模拟器。如果你有 Visual Studio 2019,它应该已经安装。如果您有 VS19 但尚未安装,您应该打开 Visual Studio 安装程序并修改您的 VS19 安装。在“Individual Components”下,您应该能够找到“Azure Storage Emulator”。如果您没有 VS19,可以在此处获取。

接下来,我建议从此处下载 Azure 存储资源管理器。如果模拟器正在运行并且您没有更改存储模拟器的默认端口,您应该能够在本地和附加 > 存储帐户 >(模拟器 - 默认端口)下找到默认条目。

使用存储资源管理器,您可以展开“Blob 容器”。右键单击“Blob Containers”,然后选择“Create Blob Container”,并为其命名。对于我的示例,我将其命名为“youtube-files”。我还创建了另一个容器,称为“youtube-files-descriptions”。

创建容器

现在来看看实际的功能。我给了自己使用依赖注入的自由(我只是害怕静态混乱)。为此,您必须包含 NuGet 包 Microsoft.Azure.Functions.Extensions 和 Microsoft.Extensions.DependencyInjection。

启动
我们在这里注册我们的服务和其他东西。我将添加一个 InternalYoutubeService(这样命名,以免与 Goodle API 提供的服务混淆)。您可以在此处阅读有关 DI 和 Azure 函数的更多信息。

// Notice that the assembly definition comes before the namespace
[assembly: FunctionsStartup(typeof(FunctionApp1.Startup))]
namespace FunctionApp1
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            // You can of course change service lifetime as needed
            builder.Services.AddTransient<IInternalYoutubeService, InternalYoutubeService>();
        }
    }
}

BlobFunction
您不必添加/注册包含任何 azure 函数的类,它们会自动处理。BlobTrigger注意和之间的区别BlobBlobTrigger规定只有当文件上传到“youtube-files”容器时,该函数才会真正触发;同时,它会在“youtube-files-descriptions”容器中查找一个文件,该文件的文件名与BlobTrigger. . 如果找不到 blob,它将返回 null,并且绑定的字符串description将为 null。您可以在此处找到不同的可用绑定。BlobTriggerBlob属性。

[StorageAccount("AzureWebJobsStorage")]
public class BlobFunction
{
    private readonly IInternalYoutubeService _YoutubeService;
    private readonly ILogger _Logger;

    // We inject the YoutubeService    
    public BlobFunction(IInternalYoutubeService youtubeService, ILogger<BlobFunction> logger)
    {
        _YoutubeService = youtubeService;
        _Logger = logger;
    }

    [FunctionName("Function1")]
    public async Task Run(
        [BlobTrigger("youtube-files/{filename}.{extension}")] Stream blob,
        [Blob("youtube-files-descriptions/{filename}-description.txt")] string description,
        string filename,
        string extension)
    {
        switch (extension)
        {
            case "mp4":
                await _YoutubeService.UploadVideo(blob, filename, description, "Some tag", "Another tag", "An awesome tag");
                break;

            case "mp3":
                await _YoutubeService.UploadAudio(blob, filename, description);
                break;

            default:
                _Logger.LogInformation($"{filename}.{extension} not handled");
                break;
        }
    }
}

YoutubeService
将包含处理实际身份验证(您正在使用的 OAuth2)和文件上传的逻辑。您可以参考@Bryan Lewis 的回答,了解如何使用传入的Stream. 我们可以将凭据存储在函数应用的配置中并注入 IConfiguration 接口,这允许我们通过提供配置中定义的值的键来访问值。这样您就可以避免在代码中硬编码任何凭据。我省略了特定于 YouTube 的上传逻辑,因为我对您使用的库没有经验,但它应该足够简单,可以将逻辑迁移到服务。

public interface IInternalYoutubeService
{
    Task UploadVideo(Stream stream, string title, string description, params string[] tags);
    Task UploadAudio(Stream stream, string title, string description, params string[] tags);
}

internal class InternalYoutubeService : IInternalYoutubeService
{
    private readonly IConfiguration _Configuration;
    private readonly ILogger _Logger;

    public InternalYoutubeService(IConfiguration configuration, ILogger<InternalYoutubeService> logger)
    {
        _Configuration = configuration;
        _Logger = logger;
    }

    public async Task UploadAudio(Stream stream, string title, string description, params string[] tags)
    {
        _Logger.LogInformation($"{_Configuration["YoutubeAccountName"]}");
        _Logger.LogInformation($"{_Configuration["YoutubeAccountPass"]}");
        _Logger.LogInformation($"Bytes: {stream.Length} - {title} - {description} - {string.Join(", ", tags)}");
    }

    public async Task UploadVideo(Stream stream, string title, string description, params string[] tags)
    {
        _Logger.LogInformation($"{_Configuration["YoutubeAccountName"]}");
        _Logger.LogInformation($"{_Configuration["YoutubeAccountPass"]}");
        _Logger.LogInformation($"Bytes: {stream.Length} - {title} - {description} - {string.Join(", ", tags)}");
    }
}

local.settings.json
当您在本地完成测试时,您当然会将这些值放入 Azure 门户上的函数应用配置中,存储字符串除外。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "YoutubeAccountName": "MyAccountName",
    "YoutubeAccountPass": "MySecretPassword"
  }
}

测试
我正在使用一个简单的文本文件“Test File-description.txt”进行测试,其中包含文本“This is a sample description.”。我还有一个 ~5MB MP3 文件,“Test File.mp3”。我首先将我的文本文件拖放到“youtube-files-descriptions”容器中,然后将“Test File.mp3”文件拖放到“youtube-files”容器中。该功能不是通过上传文本文件触发的;直到我上传函数触发的“Test File.mp3”。我看到记录了以下几行:

Executing 'Function1' (Reason='New blob detected: youtube-files/Test File.mp3', Id=50a50657-a9bb-41a5-a7d5-2adb84477f69)
MyAccountName
MySecretPassword
Bytes: 5065849 - Test File - This is a sample description. -
Executed 'Function1' (Succeeded, Id=50a50657-a9bb-41a5-a7d5-2adb84477f69)

推荐阅读