Как изменить код, чтобы превратить триггер HTTP в триггер Blob? - PullRequest
1 голос
/ 22 февраля 2020

У меня есть эта функция, которая в основном загружает видео на YouTube. Однако в настоящее время мне приходится жестко кодировать фактический путь к файлу, в котором находится видео (пример: @ "C: \ Users \ Peter \ Desktop \ audio \ test.mp4";). Мне интересно, есть ли способ сделать это более динамичным c. Например, сейчас я использую триггер 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"]
  }
}

Ответы [ 2 ]

1 голос
/ 22 февраля 2020

У меня было немного свободного времени, и, несмотря на то, что я использовал функции Azure более года, у меня никогда не было возможности реализовать BlobTrigger. Поэтому я решил поделиться своей крошечной реализацией теста ядра 3.0 и добавить ответ с избыточным количеством слов в качестве дополнения к правильному ответу @Bryan Lewis.

Если вы хотите проверить это перед запуском Azure, вы Сначала убедитесь, что у вас есть Azure Storage Emulator. Если у вас есть Visual Studio 2019, он должен быть уже установлен. Если у вас VS19, но он не установлен, вы должны открыть установщик Visual Studio и изменить установку VS19. В разделе «Отдельные компоненты» вы должны найти «Azure Storage Emulator». Если у вас нет VS19, вы можете получить его здесь .

Далее я бы порекомендовал загрузить Azure Storage Explorer из здесь . Если эмулятор работает, и вы не изменили порты по умолчанию для эмулятора хранилища, вы сможете найти запись по умолчанию в разделе Локальные и подключенные> Учетные записи хранения> (Эмулятор - Порты по умолчанию).

Использование В Storage Explorer вы можете развернуть «Blob Containers». Щелкните правой кнопкой мыши «Контейнеры BLOB-объектов», выберите «Создать контейнер BLOB-объектов» и дайте ему имя. Для моего примера я назвал это «youtube-files». Я также создал другой контейнер, назвав его «youtube-files-description».

Create container

Теперь для самой функции. Я позволил себе сделать это с помощью инъекций зависимостей (я просто боюсь хаоса). Для этого вам нужно будет включить пакет NuGet Microsoft. Azure .Functions.Extensions и Microsoft.Extensions.DependencyInjection.

Запуск
Мы регистрируем наши услуги и что- не здесь. Я добавлю InternalYoutubeService (названный так, чтобы не путать его с тем, который предоставляется API-интерфейсами Goodle). Вы можете узнать больше о 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-description» для файла с тем же именем файла, что и входящее из BlobTrigger, но с суффиксом «-description» и только если он использует ». TXT "расширение. Если 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 void 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
Будет содержать лог c, который Вы будете обрабатывать фактическую аутентификацию (используемый вами OAuth2) и загрузку файла. Вы можете обратиться к ответу @Bryan Lewis с точки зрения того, как использовать входящий Stream. Мы могли бы сохранить наши учетные данные в конфигурации нашего функционального приложения и внедрить интерфейс IConfiguration, который позволяет нам получать доступ к значениям, предоставляя ключ значения, определенный в конфигурации. Таким образом вы избегаете жесткого кодирования любых учетных данных в вашем коде. Я пропустил спецификацию YouTube c upload logi c, поскольку у меня нет опыта работы с библиотекой, которую вы используете, но она должна быть достаточно простой для переноса logi c в службу.

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», содержащего текст «Это пример описания ".. У меня также есть файл MP3 ~ 5MB, "Test File.mp3". Я начинаю с перетаскивания моего текстового файла в контейнер «описания файлов youtube», а затем с перетаскиванием «Тестовый файл». 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)
0 голосов
/ 22 февраля 2020

Это довольно прямое изменение. Просто поменяйте местами в Blob Trigger. Замените:

public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log)

на

public static void Run([BlobTrigger("blob-container-name/{fileName}")] Stream videoBlob, string fileName, ILogger log)

Это даст вам поток (videoBlob) для работы (и имя файла видео, если вам это нужно). Затем замените этот новый поток на ваш FileStream. Похоже, что вы использовали пример кода Google / YouTube для создания своей функции, но нет особой необходимости создавать отдельный метод Run () для функции Azure. Вы можете упростить ситуацию, но объедините свой метод Run () в код основной функции вместо вызова «await Run ();».

Измените свою функцию следующим образом:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;


namespace UploadVideoBlob
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task Run([BlobTrigger("video/{name}", 
            Connection = "DefaultEndpointsProtocol=https;AccountName=uploadvideoblob;AccountKey=XXXX;EndpointSuffix=core.windows.net")]Stream videoBlob, 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,
                    // This OAuth 2.0 access scope allows an application to upload files to the
                    // authenticated user's YouTube channel, but doesn't allow other types of access.
                    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"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
            video.Status = new VideoStatus();
            video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
            var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", videoBlob, "video/*");
            await videosInsertRequest.UploadAsync();
        }
    }
}

Использование событий ProgressChanged и ResponseReceived на самом деле не требуется для функции, кроме как для ведения журнала, поэтому вы можете сохранить их или устранить их. Пример Google - это консольное приложение, поэтому оно выводит на консоль много информации о состоянии.

Я также внес одно дополнительное изменение, чтобы исправить путь к файлу для вашего client_secrets. json. В этом коде предполагается, что файл json находится в том же каталоге, что и ваша функция, и публикуется вместе с функцией.

...