c# - 如何重构代码以将 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"]
}
}
解决方案
我有一些空闲时间,尽管使用 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
注意和之间的区别Blob
。BlobTrigger
规定只有当文件上传到“youtube-files”容器时,该函数才会真正触发;同时,它会在“youtube-files-descriptions”容器中查找一个文件,该文件的文件名与BlobTrigger
. . 如果找不到 blob,它将返回 null,并且绑定的字符串description
将为 null。您可以在此处找到不同的可用绑定。BlobTrigger
Blob
属性。
[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)
推荐阅读
- python - 根据 N 个数组项查找嵌套字典值
- c# - 从数组中获取多个相同长度的字符串
- sql - 根据条件插入表值的每一列
- excel - Visual Studio 说 -> Imports 'Microsoft.Office.Interop.Excel' 不包含任何公共成员或找不到
- lua - 使用 Lua 返回所需数据的问题
- raku - 在 Raku 中,不能从同一文件中定义的类继承方法特征
- xamarin.ios - 条带终端连接令牌已被兑换
- android - 在通过导航组件添加到动作的动画完成之前,防止单击视图
- python - 类型错误字符串索引必须是整数,而不是 str
- python - Django:取消订单时无法调整库存