Search code examples
c#asp.net-mvcazurefile-uploadazure-storage

Uploaded file from ASP.NET to Azure blob is empty


I am not sure to understand what can be the problem. I am developing a simple test which is Upload an image to my Azure storage. However, the file exist, but is null on the storage. It seems like the upload doesn't work and I don't understand why. I have this controller:

Create.cshtml.cs

namespace CoreWebApp.Pages
{
    public class CreateModel : PageModel
    {
        public void OnGet()
        {
        }

        [BindProperty]
        public CountryForm Country { get; set; }

        [HttpPost("CreateCountry")]
        public async Task<IActionResult> OnPostAsync(IFormFile file)
        {
            if (!ModelState.IsValid)
                return Page();

            /*var errors = ModelState.Where(x => x.Value.Errors.Count > 0)
                                    .Select(x => new { x.Key, x.Value.Errors })
                                    .ToArray();*/

            var filePath = Path.GetTempFileName();
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);

                string pictureUrl = Shared.AzureCloud.AzureCDN.GetAzureCDNInstance().UploadFile(stream, file.Name);
                try
                {
                    if (pictureUrl != null)
                        Shared.Database.SqlAction.CountriesTable.AddCountry(new Country()
                        {
                            Name = Country.Name,
                            PictureUrl = pictureUrl
                        });
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }

            return Page();
        }
    }
}

Create.cshtml

@page
@model CreateModel
@{
    ViewData["Title"] = "Create";    
}

<div class="container">
  <div class="row">
    <div class="col-lg-3" style="background-color:#FF0000;">
        <h4>Add a Country</h4>
        <form class="form form-horizontal" method="post" enctype="multipart/form-data" asp-controller="Create">
          <div asp-validation-summary="All"></div>
          <div class="row">
            <div class="col-md-12">
              <div class="form-group">
                <label asp-for="Country.Name" class="col-md-3 right">Name:</label>
                <div class="col-md-9">
                  <input asp-for="Country.Name" class="form-control" />
                  <span asp-validation-for="Country.Name"></span>
                </div>
              </div>
              <div class="form-group">
                <div class="col-md-10">
                    <p>Picture</p>
                    <input type="file" name="file" />
                </div>
              </div>
            </div>
          </div>
          <div class="row">
            <div class="col-md-12">
              <button type="submit">Create</button>
            </div>
          </div>
        </form>
    </div>
    <div class="col-*-*"></div>
  </div>
  <div class="row">
    <div class="col-*-*"></div>
    <div class="col-*-*"></div>
    <div class="col-*-*"></div>
  </div>
  <div class="row">
    ...
  </div>
</div>

My AzureCDN class is just an encapsulation:

namespace Shared.AzureCloud
{
    public class AzureCDN
    {
        private CloudStorageAccount storageAccount { get; set; }
        private CloudBlobClient blobClient { get; set; }
        private CloudBlobContainer container { get; set; }
        private CloudBlockBlob blockBlob { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Shared.AzureCloud.AzureCDN"/> class.
        /// </summary>
        public AzureCDN()
        {
            // Retrieve storage account from connection string.
            storageAccount = CloudStorageAccount.Parse(String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
                                                                                         Shared.Constants.Azure.AccountName, Shared.Constants.Azure.AccountKey));
            // Create the blob client.
            blobClient = storageAccount.CreateCloudBlobClient();

            // Retrieve a reference to a container.
            container = blobClient.GetContainerReference("eyesmedias");

            // Create the container if it doesn't already exist.
            container.CreateIfNotExistsAsync();
        }

        /// <summary>
        /// Uploads the file.
        /// </summary>
        /// <returns>The file.</returns>
        /// <param name="fileStream">File stream.</param>
        /// <param name="fileName">File name.</param>
        public string UploadFile(FileStream fileStream, string fileName)
        {
            // Retrieve reference to a blob named {name}.
            blockBlob = container.GetBlockBlobReference(fileName);

            try
            {
                // Create or overwrite the {name} "blob with contents from a local file.
                blockBlob.UploadFromStreamAsync(fileStream);
                return (blockBlob.Uri.ToString());

            } catch (Exception e)
            {
                throw e;
            }
        }

        #region Singletown part

        /// <summary>
        /// The instance.
        /// </summary>
        private static AzureCDN Instance = null;

        /// <summary>
        /// Gets the azure CDN Instance.
        /// </summary>
        /// <returns>The azure CDNI nstance.</returns>
        public static AzureCDN GetAzureCDNInstance()
        {
            if (Instance == null)
            {
                Instance = new AzureCDN();
            }
            return (Instance);
        }

        /// <summary>
        /// Sets the azure CDN Instance.
        /// </summary>
        /// <param name="instance">Instance.</param>
        public static void SetAzureCDNInstance(AzureCDN instance)
        {
            Instance = instance;
        }

        /// <summary>
        /// Init this instance.
        /// </summary>
        public static void Init()
        {
            Instance = new AzureCDN();
        }

        #endregion
    }
}

The thing is, blockBlob.UploadFromStreamAsync(fileStream); seems to be ok because it doesn't throw any exception and the path is well returned, however, even if the file is on my CDN, it is empty, unlike the file I'm selecting, on my mac, from the ASP.NET page.

I'm pretty new on ASP.NET (I began 2 days ago), advice about uploading file from web app ASP.NET are also welcome :)

Thanks for any help !


Solution

  • After you copied the file to the stream, you have to set the position of the stream to the beginning again using stream.Seek(0, SeekOrigin.Begin); (See the docs) so the actual content will be uploaded:

            [HttpPost("CreateCountry")]
            public async Task<IActionResult> OnPostAsync(IFormFile file)
            {
                if (!ModelState.IsValid)
                    return Page();
    
                /*var errors = ModelState.Where(x => x.Value.Errors.Count > 0)
                                        .Select(x => new { x.Key, x.Value.Errors })
                                        .ToArray();*/
    
                var filePath = Path.GetTempFileName();
                using (var stream = new FileStream(filePath, FileMode.Create))
                {
                    await file.CopyToAsync(stream);
    
                    stream.Seek(0, SeekOrigin.Begin); 
    
                    string pictureUrl = Shared.AzureCloud.AzureCDN.GetAzureCDNInstance().UploadFile(stream, file.Name);
                    try
                    {
                        if (pictureUrl != null)
                            Shared.Database.SqlAction.CountriesTable.AddCountry(new Country()
                            {
                                Name = Country.Name,
                                PictureUrl = pictureUrl
                            });
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e);
                    }
                }
    
                return Page();
            }
    

    If you do not do this than you are uploading a stream in which the current position is already at the end so an empty file will be uploaded.