Uploading Files In ASP.net Core

Uploading files in ASP.net core is largely the same as standard full framework MVC, with the large exception being how you can now stream large files. We will go over both methods of uploading a file in ASP.net core.

Model Binding IFormFile (Small Files)

When uploading a file via this method, the important thing to note is that your files are uploaded in their entirety before execution hits your controller action. What this means is that the disk on your server holds a temporary file while you decide where to push it. With this small files this is fine, larger files you run into issues of scale. If you have many users all uploading large files you are liable to run out of ram (where the file is stored before moving it to disk), or disk space itself.

For your HTML, it should look something like this :

The biggest thing to note is that the the encoding type is set to “multipart/form-data”, if this is not set then you will go crazy trying to hunt down why your file is showing up in your controller.

Your controller action is actually very simple. It will look something like :

Note that the name of the parameter “files” should match the name on the input in HTML.

Other than that, you are there and done. There is nothing more than you need to do.

Streaming Files (Large Files)

Instead of buffering the file in its entirety, you can stream the file upload. This does introduce challenges as you can no longer use the built in model binding of ASP.net core. Various tutorials out there show you how to get things working with massive pieces of code, but I’ll give you a helper class that should alleviate most of the work. Most of this work is taken from Microsoft’s tutorial on file uploads here. Unfortunately it’s a bit all over the place with helper classes that you need to dig around the web for.

First, take this helper class and stick it in your project. This code is taken from a Microsoft project here.

Next, you can need this extension class which again is taken from Microsoft code but moved into a helper so that it can be reused and it allows your controllers to be slightly cleaner. It takes an input stream which is where your file will be written to. This is kept as a basic stream because the stream can really come from anywhere. It could be a file on the local server, or a stream to AWS/Azure etc

Now, you actually need to create a custom action attribute that completely disables form binding. This is important otherwise C# will still try and load the contents of the request regardless. The attribute looks like :

Next, your controller should look similar to the following. We create a stream and pass it into the StreamFile extension method. The output is our FormValueProvider which we use to bind out model manually after the file streaming. Remember to put your custom attribute on the action to force the request to not bind.

In my particular example I have created a stream that writes to a temp file, but obviously you can do anything you want with it. The model I am using is the following :

Again, nothing special. It’s just showing you how to bind a view model even when you are streaming files.

My HTML form is pretty standard :

And that’s all there is to it. Now when I upload a file, it is streamed straight to my target stream which could be an upload stream to AWS/Azure or any other cloud provider, and I still managed to get my view model out too. The biggest downside is ofcourse that the viewmodel details are not available until the file has been streamed. What this means is that if there is something in the viewmodel that would normally determine where the file goes, or the name etc, this is not available to you without a bit of rejigging (But it’s definitely doable).

 

3 comments

  1. I came here from your comment in docs.microsoft.com for file uploads. Looking through what you’ve pieced together here, I notice the lack of the antiforgery token attribute and the disabling of the binding attribute. Was that intentional as they are probably a good thing but not necessary? I see you discuss the binding issue but no attribute. And testing your code here appears to work just fine without it.
    Thanks!

    1. Woops. Two things, you do need the disabling of binding attribute (This is very important otherwise the form is bound on the request anyway, will fix this, I had it earlier but didn’t copy it across into the blog post!). Secondly, the antiforgery token can be used here regardless as long as it’s sent in a header rather than the body (If you are using something like Angular you are likely doing this already).

      1. in my testing, I came to that conclusion as well regarding the non-binding attribute. thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *