Azure C# WebJob using ImageResizer not Properly Setting Content-Type

会有一股神秘感。 提交于 2019-11-30 07:41:26

问题


I'm working on an Azure WebJob to to resize newly uploaded images. The resizing works, but the newly created images do not have their content type properly set in Blob Storage. Instead they are listed application/octet-stream. Here the code handling the resizing:

public static void ResizeImagesTask(
    [BlobTrigger("input/{name}.{ext}")] Stream inputBlob,
    string name,
    string ext,
    IBinder binder)
{
    int[] sizes = { 800, 500, 250 };
    var inputBytes = inputBlob.CopyToBytes();
    foreach (var width in sizes)
    {
        var input = new MemoryStream(inputBytes);
        var output = binder.Bind<Stream>(new BlobAttribute($"output/{name}-w{width}.{ext}", FileAccess.Write));

        ResizeImage(input, output, width);
    }
}

private static void ResizeImage(Stream input, Stream output, int width)
{
    var instructions = new Instructions
    {
        Width = width,
        Mode = FitMode.Carve,
        Scale = ScaleMode.Both
    };
    ImageBuilder.Current.Build(new ImageJob(input, output, instructions));
}

My question is where and how would I set the content-type? Is it something I have to do manually, or is there an error in how I'm using the library preventing it from assigning the same content-type as the original (the behavior the library says it should exhibit)?

Thanks!

FINAL UPDATE

Thanks to Thomas for his help in arriving at the final solution, here it is!

public class Functions
{
    // output blolb sizes
    private static readonly int[] Sizes = { 800, 500, 250 };

    public static void ResizeImagesTask(
        [QueueTrigger("assetimage")] AssetImage asset,
        string container,
        string name,
        string ext,
        [Blob("{container}/{name}_master.{ext}", FileAccess.Read)] Stream blobStream,
        [Blob("{container}")] CloudBlobContainer cloudContainer)
    {

        // Get the mime type to set the content type
        var mimeType = MimeMapping.GetMimeMapping($"{name}_master.{ext}");

        foreach (var width in Sizes)
        {
            // Set the position of the input stream to the beginning.
            blobStream.Seek(0, SeekOrigin.Begin);

            // Get the output stream
            var outputStream = new MemoryStream();
            ResizeImage(blobStream, outputStream, width);

            // Get the blob reference
            var blob = cloudContainer.GetBlockBlobReference($"{name}_{width}.{ext}");

            // Set the position of the output stream to the beginning.
            outputStream.Seek(0, SeekOrigin.Begin);
            blob.UploadFromStream(outputStream);

            // Update the content type =>  don't know if required
            blob.Properties.ContentType = mimeType;
            blob.SetProperties();
        }
    }

    private static void ResizeImage(Stream input, Stream output, int width)
    {
        var instructions = new Instructions
        {
            Width = width,
            Mode = FitMode.Carve,
            Scale = ScaleMode.Both
        };
        var imageJob = new ImageJob(input, output, instructions);

        // Do not dispose the source object
        imageJob.DisposeSourceObject = false;
        imageJob.Build();
    }

    public static void PoisonErrorHandler([QueueTrigger("webjobs-blogtrigger-poison")] BlobTriggerPosionMessage message, TextWriter log)
    {
        log.Write("This blob couldn't be processed by the original function: " + message.BlobName);
    }
}

public class AssetImage
{
    public string Container { get; set; }

    public string Name { get; set; }

    public string Ext { get; set; }
}

public class BlobTriggerPosionMessage
{
    public string FunctionId { get; set; }
    public string BlobType { get; set; }
    public string ContainerName { get; set; }
    public string BlobName { get; set; }
    public string ETag { get; set; }
}

回答1:


You can't set the content type from your implementation:

You need to access the CloudBlockBlob.Properties.ContentType property:

CloudBlockBlob blob = new CloudBlockBlob(...);
blob.Properties.ContentType = "image/...";
blob.SetProperties();

Azure Webjob SDK support Blob Binding so that you can bind directly to a blob.

In your context, you want to bind to an input blob and create multiple output blobs.

  • Use the BlobTriggerAttribute for the input.
  • Use the BlobTriggerAttribute to bind to your output blobs. Because you want to create multiple outputs blobs, you can bind directly to the output container.

The code of your triggered function can look like that:

using System.IO;
using System.Web;
using ImageResizer;
using Microsoft.Azure.WebJobs;
using Microsoft.WindowsAzure.Storage.Blob;

public class Functions
{
    // output blolb sizes
    private static readonly int[] Sizes = {800, 500, 250};

    public static void ResizeImage(
        [BlobTrigger("input/{name}.{ext}")] Stream blobStream, string name, string ext
        , [Blob("output")] CloudBlobContainer container)
    {
        // Get the mime type to set the content type
        var mimeType = MimeMapping.GetMimeMapping($"{name}.{ext}");
        foreach (var width in Sizes)
        {
            // Set the position of the input stream to the beginning.
            blobStream.Seek(0, SeekOrigin.Begin);

            // Get the output stream
            var outputStream = new MemoryStream();
            ResizeImage(blobStream, outputStream, width);

            // Get the blob reference
            var blob = container.GetBlockBlobReference($"{name}-w{width}.{ext}");

            // Set the position of the output stream to the beginning.
            outputStream.Seek(0, SeekOrigin.Begin);
            blob.UploadFromStream(outputStream);

            // Update the content type
            blob.Properties.ContentType = mimeType;
            blob.SetProperties();
        }
    }

    private static void ResizeImage(Stream input, Stream output, int width)
    {
        var instructions = new Instructions
        {
            Width = width,
            Mode = FitMode.Carve,
            Scale = ScaleMode.Both
        };
        var imageJob = new ImageJob(input, output, instructions);

        // Do not dispose the source object
        imageJob.DisposeSourceObject = false;
        imageJob.Build();
    }
}

Note the use of the DisposeSourceObject on the ImageJob object so that we can read multiple time the blob stream.

In addition, you should have a look at the Webjob documentation about BlobTrigger: How to use Azure blob storage with the WebJobs SDK

The WebJobs SDK scans log files to watch for new or changed blobs. This process is not real-time; a function might not get triggered until several minutes or longer after the blob is created. In addition, storage logs are created on a "best efforts" basis; there is no guarantee that all events will be captured. Under some conditions, logs might be missed. If the speed and reliability limitations of blob triggers are not acceptable for your application, the recommended method is to create a queue message when you create the blob, and use the QueueTrigger attribute instead of the BlobTrigger attribute on the function that processes the blob.

So it could be better to trigger message from a queue that just send the filename, you can bind automatically the input blob to the message queue :

using System.IO;
using System.Web;
using ImageResizer;
using Microsoft.Azure.WebJobs;
using Microsoft.WindowsAzure.Storage.Blob;

public class Functions
{
    // output blolb sizes
    private static readonly int[] Sizes = { 800, 500, 250 };

    public static void ResizeImagesTask1(
        [QueueTrigger("newfileuploaded")] string filename,
        [Blob("input/{queueTrigger}", FileAccess.Read)] Stream blobStream,
        [Blob("output")] CloudBlobContainer container)
    {
        // Extract the filename  and the file extension
        var name = Path.GetFileNameWithoutExtension(filename);
        var ext = Path.GetExtension(filename);

        // Get the mime type to set the content type
        var mimeType = MimeMapping.GetMimeMapping(filename);

        foreach (var width in Sizes)
        {
            // Set the position of the input stream to the beginning.
            blobStream.Seek(0, SeekOrigin.Begin);

            // Get the output stream
            var outputStream = new MemoryStream();
            ResizeImage(blobStream, outputStream, width);

            // Get the blob reference
            var blob = container.GetBlockBlobReference($"{name}-w{width}.{ext}");

            // Set the position of the output stream to the beginning.
            outputStream.Seek(0, SeekOrigin.Begin);
            blob.UploadFromStream(outputStream);

            // Update the content type =>  don't know if required
            blob.Properties.ContentType = mimeType;
            blob.SetProperties();
        }
    }

    private static void ResizeImage(Stream input, Stream output, int width)
    {
        var instructions = new Instructions
        {
            Width = width,
            Mode = FitMode.Carve,
            Scale = ScaleMode.Both
        };
        var imageJob = new ImageJob(input, output, instructions);

        // Do not dispose the source object
        imageJob.DisposeSourceObject = false;
        imageJob.Build();
    }
}


来源:https://stackoverflow.com/questions/36874081/azure-c-sharp-webjob-using-imageresizer-not-properly-setting-content-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!