By now this is nothing new, but then again this topic of file uploads does seem to continue coming up once in a while. In this post I would simply like to share a few tips for handling file uploads using ASP.NET Web API2 and Azure Blob Storage.
There are numerous articles and even Microsoft examples such as this classic one and this one on the web that show you how to upload files using ASP.NET Web API. However, I found that most of them show you how to upload files in the context of having a server with an accessible file system. And if you have already tried some of these examples, you quickly found out that they are not very cloud friendly as they rely on the existence of a file system on the server, which in these days of Azure App Services are pretty much non-existent. If this is the predicament you find yourself in, as I have before in the past, then I hope these tips will help as you continue your journey into the cloud.
The Moving Parts
The scenario I have most often faced is made up of the following components:
- An ASP.NET Web API 2
- A local or cloud based Azure Storage Account
This is the most common scenario I have encountered, thus my code snippets will center around this solution.
I will keep things very simply here. The vanilla file upload scenario from the client’s perspective usually looks something like the following:
Here I assume that the front end code and markup is hosted on the same application as the Web API, just to keep things simple. If that’s not the case, simply update the action value to the URL where your file upload endpoint is hosted. That’s all there is to the client.
The Upload Endpoint
The upload endpoint should be decorated with the HttpPost and for our purposes the file upload method UploadImage will take no arguments:
NOTE: If you already know all there is to know about the ‘file input type’ then please feel free to skip this paragraph. So, I would like to make a quick pause here to remind ourselves that uploading a file through a browser is really not the same as sending a traditional array of bytes over the wire. I want to mention this, even if this may seem obvious, because I still get a lot of questions from various folks whenever I implement a file upload functionality. At one time, I even had someone ask me why I could not just send a classic .NET like byte array to the endpoint as a parameter and then read that into a stream and retrieve the file…This person thought I was trying to make her job difficult on purpose, which was not the case at all. So to settle the matter and quench any debates that you may encounter, the simple answer is that browser manufacturers implement the** file input type** according to the spec: https://www.w3.org/TR/html-markup/input.file.html and thus whenever anyone uploads files through the browser they need to leverage the attribute enctype=”multipart/form-data” and thus your network traffic as you upload the file will look a bit different than just a simple stream of bytes.
As soon as the endpoint gets hit I can proceed to check the contents of the request to see if we indeed are uploading a file through the browser. You may want to do some further checking here, depending on your business and security requirements:
Now I read the contents of the file:
So when I first implemented something like the above, I made the mistake of not using the Task Factory, which after a number of uploads I noticed that my files were not being uploaded and the endpoint was silently failing. As it turns out I was launching the ReadAsMultipartAsync on the same thread as the previous uploads and deadlocking it. It wasn’t until I stumbled upon this answer on StackOverflow that I was able to correct my mistake and things were working again: http://stackoverflow.com/questions/15201255/request-content-readasmultipartasync-never-returns Note that this code can be further simplified with the use of async and await, but i find that for sample purposes this snippet is a bit more concise to grock at first. Btw, I gotta thank yeejuto for the answer to this issue!
Now we can retrieve the file contents from the Http content parts:
Note that in the client markup I set the name as file_data in order to make it simpler to see the file for this example. You may get the file by using a custom predicate or in the case that you are uploading multiple files then you can enumerate the contents for instance; although doing so is beyond the scope of this post.
The last step in the endpoint saga is to upload the file contents to blob storage, in which case we simply have a private method that takes in the fileContents and uploads them to Azure Blob Storage
The full snippet for the UploadImage endpoint looks like this:
Uploading to Blob Storage
Lastly, the method that uploads the file contents to Azure is pretty much the same as the one in the Azure Blob Storage tutorial from Microsoft:
The code assumes you have setup an Azure Blob Storage connection named **StorageConnectionString **in your application configuration and makes the same assumptions as the Azure Blob Storage tutorial where it originated.
Long ago, when I first encountered the need to do a file upload implementation to a back-end, I stumbled a bit and it was a journey of trial an error. However, such journeys are what makes development fun and continues to fuel one’s curiosity while serving as a building block for more complex and useful implementations. If you are just getting started with browser client file uploads to a back-end such as an ASP.NET Web API, then I hope these small snippets will help you in your journey.
Until next time, happy streaming!