7

I am trying to upload files to Azure blob storage in .Net Core 2.1. Below is my code.

IFormFileCollection files = formCollection.Files;

foreach (var file in files)
{
    if (file.Length > 0)
    {
        _azureCloudStorage.UploadContent(cloudBlobContainer, file.OpenReadStream(), file.FileName);
    }
}

UploadContent implementation-

public async void UploadContent(CloudBlobContainer containerReference, Stream contentStream, string blobName)
{
    try
    {
        using (contentStream)
        {
            var blockBlobRef = containerReference.GetBlockBlobReference(blobName);
            //await containerReference.SetPermissionsAsync(new BlobContainerPermissions
            //{
            //    PublicAccess = BlobContainerPublicAccessType.Blob
            //});
            await blockBlobRef.UploadFromStreamAsync(contentStream);
        }
    }
    catch(Exception ex)
    {
        //Error here
    }
}

The code executes with below error-

{System.ObjectDisposedException: Cannot access a closed file. at System.IO.FileStream.get_Position() at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.get_Position() at Microsoft.AspNetCore.Http.Internal.ReferenceReadStream.VerifyPosition() at Microsoft.AspNetCore.Http.Internal.ReferenceReadStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) at Microsoft.WindowsAzure.Storage.Core.Util.StreamExtensions.WriteToAsync[T](Stream stream, Stream toStream, IBufferManager bufferManager, Nullable1 copyLength, Nullable1 maxLength, Boolean calculateMd5, ExecutionState1 executionState, StreamDescriptor streamCopyState, CancellationToken token) in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\Common\Core\Util\StreamExtensions.cs:line 301 at Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.UploadFromStreamAsyncHelper(Stream source, Nullable1 length, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext, IProgress1 progressHandler, CancellationToken cancellationToken) in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Blob\CloudBlockBlob.cs:line 352 at Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob.UploadFromStreamAsyncHelper(Stream source, Nullable1 length, AccessCondition accessCondition, BlobRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) in C:\Program Files (x86)\Jenkins\workspace\release_dotnet_master\Lib\WindowsRuntime\Blob\CloudBlockBlob.cs:line 290 at Common.AzureCloudStorage.UploadContent(CloudBlobContainer containerReference, Stream contentStream, String blobName)

Alternate solution which worked for me: adding to azure blob storage with stream

Any help with this please? Please let me know if I can provide more details.

Souvik Ghosh
  • 4,456
  • 13
  • 56
  • 78
  • 2
    Await UploadfromStreamAsync? – Ola Ekdahl Jul 08 '18 at 15:41
  • @OlaEkdahl I tried using awaiter- `blockBlobRef.UploadFromStreamAsync(contentStream).GetAwaiter()` but it didn't work too. Tried `await` too, didn't work. – Souvik Ghosh Jul 08 '18 at 15:47
  • I had a similar issue with large files and I had to increase the maxRequestLength. Maybe something here will work, https://stackoverflow.com/questions/38698350/increase-upload-file-size-in-asp-net-core. – Ola Ekdahl Jul 08 '18 at 19:44
  • 1
    @OlaEkdahl This solution for me- https://stackoverflow.com/questions/47296020/adding-to-azure-blob-storage-with-stream – Souvik Ghosh Jul 09 '18 at 03:56

5 Answers5

3

My solution was to wait until the task had completed before continuing, e.g.

    private async void SaveAsync(IFormFile file)
    {
        CloudBlockBlob blob = this.blobContainer.GetBlockBlobReference(file.FileName);
        var task = blob.UploadFromStreamAsync(file.OpenReadStream(), file.Length);

        while (task.IsCompleted == false) {
            Thread.Sleep(1000);
        }

    }

Maybe passing in the length helped as well?

Steve Smith
  • 2,244
  • 2
  • 18
  • 22
  • Why would it make a difference to pass the length as well? – Dennis Nov 16 '20 at 13:25
  • Without checking the source code for CloudBlockBlob it's hard to tell, but I'd hazard a guess that it helps the blob to know when it's finished reading the stream since it will know how many bytes it needs to read.. – Steve Smith Nov 17 '20 at 15:49
2

My solution was to include the file length and set the position = 0.

MemoryStream outStr = new MemoryStream();
CloudBlockBlob myBlob = container.GetBlockBlobReference(name);
outStr.Position = 0;
await myBlob.UploadFromStreamAsync(outStr, outStr.Length);
rajiv.cla
  • 356
  • 3
  • 7
1

Same problem, few years later (WindowsAzure.Storage version 3.0.3.0, targetFramework net45).
Synchonous UploadFromStream works, UploadFromStreamAsync does not. I would vote up to suspect Azure SDK does'nt hone async versions, rather than sdk usage deficiency. And, I am also a fairly experienced developer - too experienced to stumble now and then over a Microsoft feature which is declared, but doesn't work when scrutinized closer.
The same here for other async methods (e.g. SetPropertiesAsync). I've been debugging my method (below), and was just losing the breakpoint after every *Async method - that's how I've figured this out. And out of curiousity changed the UploadFromStreamAsync => UploadFromStream, and then SetPropertiesAsync to just SetProperties.

    public async Task StoreItem(string filename, MemoryStream content, string contentType, ICloudBlob cloudBlob)
{
    cloudBlob.UploadFromStream(content); //this line works
    //await cloudBlob.UploadFromStreamAsync(content); //doesn't work
    cloudBlob.Properties.ContentType = contentType;
    cloudBlob.SetProperties();
    //await cloudBlob.SetPropertiesAsync(); //doesn't work either
}
  • Perhaps the file is falling out of scope and being garbage collected, and closing the file before the async method executes? This seems to fit with the comment by Steve Smith (just below). I'm no expert on object lifecycle scope with async methods. – David Pierson Jun 11 '21 at 00:19
0

Setting the position to 0 and passing stream length to UploadFromStreamAsync() solved my issue (file was not getting uploaded, even if the container existed already).

using (var stream = file.OpenReadStream())
{
    var fileBlob = container.GetBlockBlobReference(fileName);
    stream.Position = 0;
    fileBlob.UploadFromStreamAsync(stream, stream.Length);
}
ak_01
  • 61
  • 1
  • 3
-1

I have tested your method UploadContent, it worked fine.

I guess your problem may be the file collection you got.

Here is my code for your reference:

using ConsoleAppCore.DAL;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using System;
using System.IO;

namespace ConsoleAppCore
{
    class Program
    {
        static void Main(string[] args)
        {
            Run();
            Console.WriteLine("Success");
            Console.ReadLine();
        }

        public static void Run()
        {
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=xxxxx;AccountKey=O7xB6ebGq8e86XQSy2vkvSi/x/exxxxxxxxxxkly1DsQPYY5dF2JrAVHtBozbJo29ZrrGJA==;BlobEndpoint=https://xxxx.blob.core.windows.net/;QueueEndpoint=https://xxxx.queue.core.windows.net/;TableEndpoint=https://xxxx.table.core.windows.net/;FileEndpoint=https://xxxx.file.core.windows.net/;");

            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = cloudBlobClient.GetContainerReference("containertest");

            container.CreateIfNotExists();
            DirectoryInfo dir = new DirectoryInfo("E://Test");

            foreach (FileInfo file in dir.GetFiles())
            {

                BlobStorage.UploadContent(container, file.OpenRead(), file.Name);

            }
        }


    }
}

And the UploadContent method in my BlobStorage class:

using Microsoft.Azure.Storage.Blob;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ConsoleAppCore.DAL
{
    public class BlobStorage
    {
        public static async void UploadContent(CloudBlobContainer containerReference, Stream contentStream, string blobName)
        {
            try
            {
                using (contentStream)
                {

                    var blockBlobRef = containerReference.GetBlockBlobReference(blobName);
                    //await containerReference.SetPermissionsAsync(new BlobContainerPermissions
                    //{
                    //    PublicAccess = BlobContainerPublicAccessType.Blob
                    //});
                    await blockBlobRef.UploadFromStreamAsync(contentStream);
                }
            }
            catch (Exception ex)
            {
                //Error here
            }
        }
    }
}
Lee Liu
  • 1,981
  • 1
  • 12
  • 13
  • 2
    You are using `FileInfo` and I am using `IFormFile`. May be that's why. I have already tried this similar code and I am able to upload a file from my local. I am just facing problem with the files submitted by the form which comes as `IFormFile` object. – Souvik Ghosh Jul 10 '18 at 10:19