Azure Blob Upload not working in ASP .Net Core Application

Viewed 2341

I am trying to upload a file to Azure blob storage in Asp .Net Core. Everything seems to be working but no file is uploaded. Here is my code snippet:

        var blobReference = GetBlobReference();
        rawData.Position = 0;

        //  var result = blobReference.UploadFromStreamAsync(rawData);
        var result = blobReference.UploadFromFileAsync("C:\\users\\tjaartb\\Downloads\\DebitOrderMandate.pdf");
        result.GetAwaiter().GetResult();
        var blobPath = blobReference.Uri.ToString();

What's happening?

The debugger steps over the result.GetAwaiter().GetResult() with no exceptions occurring. Checking the result Task variable after the GetResult() call shows that the task state is RanToCompletion and the exception property is blank. My blob container is created successfully in earlier code indicating that the connection to blob storage is successful. GetResult() finishes instantly, so it seems like nothing is happening.

Things I've checked

  • rawData is a MemoryStream that is populated with file data. Using the commented line to try and upload via a stream is not successful either.
  • The connection to Azure is working.
  • The file path exists.
  • There are no exceptions thrown.
  • The only thing in my debug window output is Started Thread <ThreadNumber>
  • Putting an invalid path in the call to blobReference.UploadFromFileAsync() throws a FileNotFoundException as expected.
  • I've tried downgrading my project to netcoreapp2.0 from netcoreapp2.1 with no success.
4 Answers

the following code with WindowsAzure.Storage - Version 9.3.1 package works on Asp Net Core 2.1.

public class AzureBlobModel
{
    public string FileName { get; set; }
    public long? FileSize { get; set; }
    public Stream Stream { get; set; }
    public string ContentType { get; set; }
}

public interface IImageStore
{
    Task<string> SaveDocument(Stream documentStream);
    Task<bool> DeleteDocument(string imageId);
    Task<AzureBlobModel> DownloadDocument(string documentId, string fileName);
    string UriFor(string documentId);
}

public class ImageStore : IImageStore
{
    CloudBlobClient blobClient;
    string baseUri = "https://xxxxx.blob.core.windows.net/";

    public ImageStore()
    {
        var credentials = new StorageCredentials("xxxxx accountName xxxxx", "xxxxx keyValue xxxxx");
        blobClient = new CloudBlobClient(new Uri(baseUri), credentials);
    }

    public async Task<string> SaveDocument(Stream documentStream)
    {
        var documentId = Guid.NewGuid().ToString();
        var container = blobClient.GetContainerReference("xxxxx container xxxxx");
        var blob = container.GetBlockBlobReference(documentId);
        await blob.UploadFromStreamAsync(documentStream);
        return documentId;
    }

    public async Task<bool> DeleteDocument(string documentId)
    {
        var container = blobClient.GetContainerReference("xxxxx container xxxxx");
        var blob = container.GetBlockBlobReference(documentId);
        bool blobExisted = await blob.DeleteIfExistsAsync();
        return blobExisted;
    }

    public async Task<AzureBlobModel> DownloadDocument(string documentId, string fileName)
    {
        var container = blobClient.GetContainerReference("xxxxx container xxxxx");
        var blob = container.GetBlockBlobReference(documentId);

        var doc = new AzureBlobModel()
        {
            FileName = fileName,
            Stream = new MemoryStream(),
        };

        doc.Stream = await blob.OpenReadAsync();
        doc.ContentType = blob.Properties.ContentType;
        doc.FileSize = blob.Properties.Length;

        return doc;
    }

    public string UriFor(string documentId)
    {
        var sasPolicy = new SharedAccessBlobPolicy
        {
            Permissions = SharedAccessBlobPermissions.Read,
            SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-15),
            SharedAccessExpiryTime = DateTime.UtcNow.AddDays(1)
        };

        var container = blobClient.GetContainerReference("xxxxx container xxxxx");
        var blob = container.GetBlockBlobReference(documentId);
        var sas = blob.GetSharedAccessSignature(sasPolicy);
        return $"{baseUri}xxxxx container xxxxx/{documentId}{sas}";
    }
}

public class DocForCreationDto
{
    public IFormFile File { get; set; }

    // other properties ...
}

// On the controller

[HttpPost]
public async Task<IActionResult> Upload([FromForm]DocForCreationDto docForCreationDto)
{
    if (docForCreationDto.File == null || !ModelState.IsValid)
    {
        return new UnprocessableEntityObjectResult(ModelState);
    }

    string documentId = string.Empty;
    using (var stream = docForCreationDto.File.OpenReadStream())
    {
        documentId = await _imageStore.SaveDocument(stream);
    }

    if (documentId != string.Empty)
    {
        // upload ok ...
        //some business logic here ...
        return Ok();
    }

    return BadRequest("xxx error xxx");
}

[HttpGet("{documentId}", Name = "GetDocument")]
public async Task<IActionResult> GetDocument(int documentId)
{
    var doc = await _imageStore.DownloadDocument(documentId, "output filename");

    return File(doc.Stream, doc.ContentType, doc.FileName);
}

// On Startup - ConfigureServices

services.AddSingleton<IImageStore, ImageStore>();

Adding a / prefix to a Blob name results in a silent failure. The file does not upload at all. removing the / prefix fixed the problem.

With .NET Core 2.1 and WindowsAzure.Storage 9.3.2 all the methods are now async. If you don't await all the async method calls you can get silent failures. The comments below the question helped me figure that out.

The interface I am implementing in that method is part of a larger ecosystem that doesn't have async signatures. It would require a lot of effort to refactor it.

You could use Wait() to replace await to implement the async signatures.Refer to the following code:

CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connection);
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
var cloudBlobContainer = cloudBlobClient.GetContainerReference("containername");
CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference("DebitOrderMandate.pdf");
using (var fileStream = System.IO.File.OpenRead("C:\\users\\tjaartb\\Downloads\\DebitOrderMandate.pdf"))
{
    blob.UploadFromStreamAsync(fileStream).Wait();
}
Related