azureazure-functions

Does the Microsoft.Azure.WebJobs.Blob Extension generate a non Null stream?


I'm following an Azure training course on LinkedIn: "Building a web application on Microsoft Azure"

I encountered an error in a section on writing an Azure function triggered by adding a file to Blob storage.

I looked up similar code in Microsoft's documentation. Writing to stream

Neither the course code or Microsoft's code work.

When I do this:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.WebJobs;

namespace Wpm
{
    public class ThumbnailCreator
    {
        [Function(nameof(ThumbnailCreator))]
        public static async Task Run(
            [Microsoft.Azure.Functions.Worker.BlobTrigger("photos/{name}", Connection = 
            "WpmStorage")]
            Stream blobStream1,
            [Microsoft.Azure.WebJobs.Blob("thumbnails/{name}", FileAccess.Write, 
            Connection = "WpmStorage")]
            Stream blobStream2)
        {
            await blobStream1.CopyToAsync(blobStream2);
        }
    }
}

I get these errors:

Executing 'Functions.ThumbnailCreator' (Reason='New blob detected(LogsAndContainerScan): photos/Kitty's cat.jpg', Id=63e38cc0-7b4d-476f-9b2c-55d1b1098bfd)

Function 'ThumbnailCreator', Invocation id '63e38cc0-7b4d-476f-9b2c-55d1b1098bfd': An exception was thrown by the invocation.

Result: Function 'ThumbnailCreator', Invocation id '63e38cc0-7b4d-476f-9b2c-55d1b1098bfd': An exception was thrown by the invocation. Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'destination')

at System.ArgumentNullException.Throw(String paramName)

at System.IO.Stream.ValidateCopyToArguments(Stream destination, Int32 bufferSize)

Executed 'Functions.ThumbnailCreator' (Failed, Id=63e38cc0-7b4d-476f-9b2c-55d1b1098bfd, Duration=129ms)

[2024-09-06T12:30:37.733Z] at System.IO.Stream.CopyToAsync(Stream destination, Int32 bufferSize, CancellationToken cancellationToken)

[2024-09-06T12:30:37.735Z] System.Private.CoreLib: Exception while executing function: Functions.ThumbnailCreator. System.Private.CoreLib: Result: Failure Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'destination')

[2024-09-06T12:30:37.736Z] at System.IO.Stream.CopyToAsync(Stream destination)

[2024-09-06T12:30:37.739Z] at System.ArgumentNullException.Throw(String paramName)

[2024-09-06T12:30:37.740Z] at Wpm.ThumbnailCreator.Run(Stream blobStream1, Stream blobStream2) in F:\Code_Library_Local\Azure Course\wpm-thumbnailcreator\Thumbnailcreator\ThumbnailCreator.cs:line 21

Which I believe means that blobStream2 is Null.

And when I use Breakpoints blobStream2 is indeed Null.

But the code in my course and the code at Microsoft imply that blobStream2 is created as not Null in the method signature.
I can't find any mention of how to initialize blobStream2.

Am I right in my analysis?

What can I do about it?


Solution

  • There are two different packages available for 2 function types:- 1. In-process Function (Microsoft.Azure.WebJobs.Extensions.*), 2. Isolated Function (Microsoft.Azure.Functions.Worker.Extensions.*). In your code, you have created an isolated function but using output binding attribute of in-process function.

    I have used given code to copy the blob using output binding of isolated function.

    using Microsoft.Azure.Functions.Worker;
    using Microsoft.Extensions.Logging;
    
    namespace _78957236
    {
        public class ThumbnailCreator
        {
            private readonly ILogger<ThumbnailCreator> _logger;
    
            public ThumbnailCreator(ILogger<ThumbnailCreator> logger)
            {
                _logger = logger;
            }
    
            [Function(nameof(ThumbnailCreator))]
            public async Task<MyOutputType> Run([BlobTrigger("samples-workitems/{name}", Connection = "BlobConnectionString")] Stream stream,
                FunctionContext context)
            {
                var logger = context.GetLogger<ThumbnailCreator>();
                var result = new MyOutputType();
    
                using (MemoryStream ms = new MemoryStream())
                {
                    await stream.CopyToAsync(ms);
                    result.Blob = ms.ToArray();
                }
    
                result.LogMessage = "Blob sample-container/sample-blob has been copied to the output blob.";
                logger.LogInformation(result.LogMessage);
    
                return result;
            }
        }
    
        public class MyOutputType
        {
            [BlobOutput("test-samples-output/sample-blob", Connection = "BlobConnectionString")]
            public byte[]? Blob { get; set; }
    
            public string? LogMessage { get; set; }
        }
    }
    

    .csproj-

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <AzureFunctionsVersion>v4</AzureFunctionsVersion>
        <OutputType>Exe</OutputType>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <RootNamespace>_78957236</RootNamespace>
      </PropertyGroup>
      <ItemGroup>
        <FrameworkReference Include="Microsoft.AspNetCore.App" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.21.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="1.2.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs" Version="6.3.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.17.0" />
        <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.22.0" />
        <PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.2.0" />
      </ItemGroup>
      <ItemGroup>
        <None Update="host.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
        <None Update="local.settings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
          <CopyToPublishDirectory>Never</CopyToPublishDirectory>
        </None>
      </ItemGroup>
      <ItemGroup>
        <Using Include="System.Threading.ExecutionContext" Alias="ExecutionContext" />
      </ItemGroup>
    </Project>
    

    I am getting expected response.

    Azure Functions Core Tools
    Core Tools Version:       4.0.5907 Commit hash: N/A +807e89766a92b14fd07b9f0bc2bea1d8777ab209 (64-bit)
    Function Runtime Version: 4.834.3.22875
    
    [2024-09-06T14:31:02.671Z] Found C:\Users\****\78957236\78957236.csproj. Using for user secrets file configuration.
    [2024-09-06T14:31:06.288Z] Azure Functions .NET Worker (PID: 27636) initialized in debug mode. Waiting for debugger to attach...
    [2024-09-06T14:31:06.341Z] Worker process started and initialized.
    
    Functions:
    
            ThumbnailCreator: blobTrigger
    
    For detailed output, run func with --verbose flag.
    [2024-09-06T14:31:11.334Z] Host lock lease acquired by instance ID '0000000000000000000000000D2022A4'.
    [2024-09-06T14:31:20.014Z] Executing 'Functions.ThumbnailCreator' (Reason='New blob detected(LogsAndContainerScan): samples-workitems/78951632.html', Id=f73379f4-fd4d-4431-8e88-2ce9faa0eab1)
    [2024-09-06T14:31:20.017Z] Trigger Details: MessageId: 54f55b73-4d21-4eac-af62-04e9b7740bea, DequeueCount: 1, InsertedOn: 2024-09-06T14:31:19.000+00:00, BlobCreated: 2024-09-06T13:38:40.000+00:00, BlobLastModified: 2024-09-06T14:31:17.000+00:00
    [2024-09-06T14:31:21.845Z] Blob sample-container/sample-blob has been copied to the output blob.
    [2024-09-06T14:31:23.591Z] Executed 'Functions.ThumbnailCreator' (Succeeded, Id=f73379f4-fd4d-4431-8e88-2ce9faa0eab1, Duration=3841ms)
    

    You need to create an in-process function if you would like to use its binding attributes. Please refer to the MS Doc for the same.