For part 1 of this guide, we’re going to create a new ASP.NET Core Web Application (.NET Core), and create a simple file upload form that loads files into our blob storage. Start off by creating a new .NET Core web app, with the Web Application template, and leave authentication out. Then create a Gallery and Upload view, and their respective actions on the HomeController. For ease of use, I added links to these views to the navbar in the _Layout to switch between the views.
<form method="post" enctype="multipart/form-data" action="/api/Upload"> <div class="form-group"> <div class="col-md-10"> <p>Upload one or more files using this form:</p> <input type="file" name="files" multiple /> </div> </div> <div class="form-group"> <div class="col-md-10"> <input type="submit" value="Upload" /> </div> </div> </form>
As you can see, we are going to be posting our form to an /api/Upload end point. Right now we don’t have an available endpoint for that form to actually hit, so next we will create one. Right click the controllers folder in your solution explorer, then click add -> new item -> ASP.NET Core -> Web API Controller Class, and rename the file to be “UploadController.cs”. This will give you the shell of the API controller that we’ll use. For the POST action, change the definition to what follows:
[HttpPost] public async Task<IActionResult> Post(List<IFormFile> files)
We’ll need the action to be an async task, as the blob functions we will be using will be awaited. Note that the parameter of the action is of type “List<IFormFile>”. This is how .NET Core by default handles files that are posted via a form. Also note that the “name” attribute of the file input matches the parameter, and that it’s a List of IFormFiles since the input has the multiple attribute. If you only want to allow one file per upload, you simply take out that attribute, and change the parameter to be of type IFormFile.
Now that we have our API set up, it’s time to get to work on the actual interfacing with the blob storage. Right click your Dependencies, go to Manage NuGet Packages, browse for “WindowsAzure.Storage”, and install the package. The plan is to abstract out all of the blob interactions into a Service class so we create a new folder called Services and a new class inside it called “BlobService.cs”. For simplicity sake, we can throw in our connection string of “UseDevelopmentStorage=true;” as a private variable for now. In a real world environment, we would want to store it in appsettings, but this is fine for now.
For our blob service code, we’ll create a helper function to get where the blob will be stored. More specifically, we’re going to create a function that allows us to get a reference to the container that will hold our blobs.
public CloudBlobContainer GetBlobContainer() { CloudStorageAccount storageAccount = CloudStorageAccount.Parse(conn); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference("myfirstcontainer"); // Create the container if it doesn't already exist. container.CreateIfNotExistsAsync(); return container; }
Finally, we’ll edit our Post action on our Upload controller to have the file be written to a new blob reference in our container.
var blobSvc = new BlobService(); var container = blobSvc.GetBlobContainer(); //long size = files.Sum(f => f.Length); foreach (var file in files) { var blob = container.GetBlockBlobReference(file.FileName); if (!blob.ExistsAsync().Result) { if (file.Length > 0) { using (var fileStream = file.OpenReadStream()) using (var ms = new MemoryStream()) { fileStream.CopyTo(ms); var fileBytes = ms.ToArray(); //string s = Convert.ToBase64String(fileBytes); // act on the Base64 data if needed await blob.UploadFromByteArrayAsync(fileBytes, 0, fileBytes.Length); blob.Properties.ContentType = file.ContentType; await blob.SetPropertiesAsync(); } } } }
Simply enough, we loop through the list of files, attempt to get a block blob reference on the uploaded file’s filename, and if it doesn’t exist, along with that the file isn’t empty, then get the byte array of the file to upload. Then it’s simply awaiting the blob.UploadFromByteArrayAsync().
It’s important to note that you lose the content types and some other properties, hence why we follow the upload with setting the content type and calling blob.SetPropertiesAsync(). This is useful for us in that we can differentiate images from non-images for when we go to display our blobs. For example, we can display all of our blobs as a list of files, or we can display our image files as the image themselves, but that’s for the next part of this guide.