ASP.NET Core – How to receive a file in a web API request

When the client posts a file in a multipart/form-data request, it’s loaded into an IFormFile object. This contains file information (such as the file name) and exposes the file content as a stream. This allows you to save the file or process it however you want to.

You can access the IFormFile object through Request.Form.Files:

[Route("[controller]")] [ApiController] public class FilesController : ControllerBase { [HttpPost] public async Task<IActionResult> Post() { IFormFile file = Request.Form.Files.FirstOrDefault(); //process file content return Ok($"Received file {file.FileName} with size in bytes {file.Length}"); } }
Code language: C# (cs)

Or you can add an IFormFile parameter (or add it as a model property), and it’ll map the file form data to the parameter by name:

[Route("[controller]")] [ApiController] public class FilesController : ControllerBase { [HttpPost] public async Task<IActionResult> Post(IFormFile file) { //process file content return Ok($"Received file {file.FileName} with size in bytes {file.Length}"); } }
Code language: C# (cs)

Note: Make sure the IFormFile parameter name matches the file’s name attribute in the form data, otherwise it won’t map and will be null.

Saving to disk

IFormFile exposes the file content as a stream. You can save this to disk by creating a FileStream and copying the file stream to it.

Here’s an example of saving the file to disk:

using System.IO; [HttpPost] public async Task<IActionResult> Post(IFormFile file) { if (file.Length <= 0) return BadRequest("Empty file"); //Strip out any path specifiers (ex: /../) var originalFileName = Path.GetFileName(file.FileName); //Create a unique file path var uniqueFileName = Path.GetRandomFileName(); var uniqueFilePath = Path.Combine(@"C:\temp\", uniqueFileName); //Save the file to disk using (var stream = System.IO.File.Create(uniqueFilePath)) { await file.CopyToAsync(stream); } return Ok($"Saved file {originalFileName} with size {file.Length / 1024m:#.00} KB using unique name {uniqueFileName}"); }
Code language: C# (cs)

Note: For simplicity, this hardcodes the file upload directory (C:\temp\). Realistically, you’d want to put this setting with the rest of your config data.

When I send a request to this, it saves the file to a dedicated file upload directory and uses a unique filename. It returns the response:

Saved file class-hierarchy-diagram.png with size 6.88 KB using unique name hseadpgk.xgf
Code language: plaintext (plaintext)

IFormFile as a model property

In many cases, you’ll want to post the file with associated data as part of a model. You can add the IFormFile as a model property.

public class Document { public string Title { get; set; } public string Version { get; set; } public IFormFile File { get; set; } }
Code language: C# (cs)

Then include the model as a parameter and apply the [FromForm] attribute:

[HttpPost] public async Task<IActionResult> Post([FromForm]Document document) { //process file return Ok($"Processed document {document.Title} v{document.Version} - {document.File.FileName}"); }
Code language: C# (cs)

It’ll load the file data into the IFormFile property and you can process / save it however you want.

Note: Include the [FromForm] attribute when mapping form data to a model, otherwise you’ll get a 415- Media Not Supported response.

File size limits

File / request size limits are different per web server. In Kestrel-hosted web apps, the main default limits are:

  • Request size limit of 30 MB.
  • Multipart form section size limit of 128 MB.

You can change the limits for all requests or per action. I recommend changing the limits per action, because you’ll probably want drastically different limits for actions that deal with file uploading compared with other actions.

Here’s an example of increasing the request size limit to 60 MB:

[HttpPost] [RequestSizeLimit(bytes: 60_000_000)] public async Task<IActionResult> Post(IFormFile file) { //process file return Ok(); }
Code language: C# (cs)

Files are sent as form data, so you also have to take the form size limits into account. It’ll use the smallest applicable limit to determine if the request has exceeded limits. You can change the form size limits with the [RequestFormLimits] attribute.

Receiving multiple files

There are two ways to receive multiple files:

  • Add an IFormFile parameter for each file.
  • Use IEnumerable<IFormFile> to receive multiple files into a single parameter.

The first option is better when you know exactly how many files you require the client to pass in. The second option is better when the client can send any number of files.

Here’s an example of receiving multiple files:

[HttpPost] public async Task<IActionResult> Post(IEnumerable<IFormFile> files) { foreach(var file in files) { //process each file } return Ok(); }
Code language: C# (cs)

When using this approach, the client will need to use the same name for each file they add to the form data.

14 thoughts on “ASP.NET Core – How to receive a file in a web API request”

    • Thanks Tomas.

      I haven’t used the minimal API approach in a real project yet. So I did a quick test. It won’t work with an IFormFile parameter. Instead, you have to accept an HttpRequest parameter into the delegate and get the file from the form data. Here’s an example of accepting a posted file:

      app.MapPost("/files", async Task<IResult> (HttpRequest request) =>
      {
      var form = await request.ReadFormAsync();
      var formFile = form.Files["file"];
      return Results.Ok(formFile.FileName);

      });

      Based on this quick test, it’s way simpler to get a file using controllers compared with using the minimal API approach.

      Reply
  1. Thaks for the post, Tomas.

    Do you have any example of how to call this endpoint from a dotnet client, like Xamarin, WinUI…?

    Kind regards!!

    Reply
  2. Your note, “…IFormFile parameter name matches the file’s name attribute…”, saved my day.
    Thank you so much!

    Reply
  3. Bonjour et Merci pour le tutoriel
    Neamoins Jai une Question

    var uniqueFilePath = Path.Combine(Config.FileUploadDirectory, uniqueFileName);

    le mot Config presente une erreur chez moi. ya t”il un import a faire pour cela

    Reply
    • Bonjour,

      Oh sorry. For this example, I added the Config class and hardcoded the path in the FileUploadDirectory property. This was meant to act as a placeholder for the reader to use their own configuration class. I can see this is too vague though.

      I’ve simplified the example by hardcoding the path:
      var uniqueFilePath = Path.Combine(@"C:\temp\", uniqueFileName);

      Thanks for asking about this.

      Reply
    • I’m happy to hear these two articles helped you with dealing with a file upload problem. And thank you for your kind feedback, I appreciate it!

      Reply
  4. Hi Mak, thank you for your excellent post

    I have tried to submit an object from client app to an API which contains some data and a file, but I failed to do so.
    (note from page author (Mak) – I removed the code here for brevity)

    Reply
  5. Hello Mak, i cant catch the files in the Method params. Its always empty, but in the request i get the files. Is there something else i have to do ?

    Reply
    • In the multipart/form request, make sure all of the file keys have the same name as the parameter.

      For example, if your method parameter is named “files”, make sure all files in the form data have their key set to “files”.

      Also, make sure you’re using the parameter type IEnumerable<IFormFile>.

      Reply

Leave a Comment