1
votes

I have an Azure blob container for storing images. I also have a suite of ASP.NET Web API methods for adding / deleting / listing the blobs in this container. This all works if I upload the images as files. But I now want to upload the images as a stream and am getting an error.

public async Task<HttpResponseMessage> AddImageStream(Stream filestream, string filename)
    {
        try
        {
            if (string.IsNullOrEmpty(filename))
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest));
            }

            BlobStorageService service = new BlobStorageService();
            await service.UploadFileStream(filestream, filename, "image/png");
            var response = Request.CreateResponse(HttpStatusCode.OK);
            return response;
        }
        catch (Exception ex)
        {
            base.LogException(ex);
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest));
        }

The code for adding a new image to the blob container as a stream looks like this.

public async Task UploadFileStream(Stream filestream, string filename, string contentType)
    {
        CloudBlockBlob blockBlobImage = this._container.GetBlockBlobReference(filename);
        blockBlobImage.Properties.ContentType = contentType;
        blockBlobImage.Metadata.Add("DateCreated", DateTime.UtcNow.ToLongDateString());
        blockBlobImage.Metadata.Add("TimeCreated", DateTime.UtcNow.ToLongTimeString());
        await blockBlobImage.UploadFromStreamAsync(filestream);
    }

And finally here's my unit test that is failing.

[TestMethod]
    public async Task DeployedImageStreamTests()
    {
        string blobname = Guid.NewGuid().ToString();

        //Arrange
        MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes($"This is a blob called {blobname}."))
        {
            Position = 0
        };

        string url = $"http://mywebapi/api/imagesstream?filestream={stream}&filename={blobname}";
        Console.WriteLine($"DeployedImagesTests URL {url}");
        HttpContent content = new StringContent(blobname, Encoding.UTF8, "application/json");
        var response = await ImagesControllerPostDeploymentTests.PostData(url, content);

        //Assert
        Assert.IsNotNull(response);
        Assert.IsTrue(response.IsSuccessStatusCode); //fails here!!
        Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
    }

The error I am getting is Value cannot be null. Parameter name: source

Is this the correct way to upload an image stream to Azure blob storage using Web API? I have it working with image files without a problem, and only getting this problem now that I'm trying to upload using streams.

1
What is the stack trace for that error you're getting (i.e., what line of code throws the error)?Josh Darnell

1 Answers

3
votes

Is this the correct way to upload an image stream to Azure blob storage using Web API? I have it working with image files without a problem, and only getting this problem now that I'm trying to upload using streams.

According to your description and error message, I found you send your stream data in your url to the web api.

According to this article:

Web API uses the following rules to bind parameters:

If the parameter is a "simple" type, Web API tries to get the value from the URI. Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. (More about type converters later.)

For complex types, Web API tries to read the value from the message body, using a media-type formatter.

In my opinion, the stream is a complex types, so I suggest you could post it as body to the web api.

Besides, I suggest you could crate a file class and use Newtonsoft.Json to convert it as json as message's content.

More details, you could refer to below codes. File class:

  public class file
    {
        //Since JsonConvert.SerializeObject couldn't serialize the stream object I used byte[] instead
        public byte[] str { get; set; }
        public string filename { get; set; }

        public string contentType { get; set; }
    }

Web Api:

  [Route("api/serious/updtTM")]
    [HttpPost]
    public void updtTM([FromBody]file imagefile)
    {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse("aaaaa");
            var client = storageAccount.CreateCloudBlobClient();
            var container = client.GetContainerReference("images");

            CloudBlockBlob blockBlobImage = container.GetBlockBlobReference(imagefile.filename);
            blockBlobImage.Properties.ContentType = imagefile.contentType;
            blockBlobImage.Metadata.Add("DateCreated", DateTime.UtcNow.ToLongDateString());
            blockBlobImage.Metadata.Add("TimeCreated", DateTime.UtcNow.ToLongTimeString());

            MemoryStream stream = new MemoryStream(imagefile.str)
            {
                Position=0
            };
            blockBlobImage.UploadFromStreamAsync(stream);
        }

Test Console:

 using (var client = new HttpClient())
            {
                string URI = string.Format("http://localhost:14456/api/serious/updtTM");
                file f1 = new file();

                byte[] aa = File.ReadAllBytes(@"D:\Capture2.PNG");

                f1.str = aa;
                f1.filename = "Capture2";
                f1.contentType = "PNG";
                var serializedProduct = JsonConvert.SerializeObject(f1); 
                var content = new StringContent(serializedProduct, Encoding.UTF8, "application/json");
                var result = client.PostAsync(URI, content).Result;
            }