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 content 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.

Now send a request containing file content to this endpoint. 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.xgfCode 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.

16 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
  6. Thank you, Mak. Could you help me in how to make this file appear in the Method Post if it is stored in physical folder instead of being in the database? I made an application in which I get avatar image of the user, except it in physical folder, but do not konow how it will be displayed again to the user there in the avatar, I must create a new Controller for viewing?
    Another question, is about having made my Method Post to recdeive the image, however when I will test on Postman or Thunder Client, is giving the error 415, and sometimes 500, saying That I am getting a null body. Would you know how to help me?

    Reply
    • Hi,

      1. In general, to display an image, use an img tag pointing to the image’s file path (relative or absolute). For example, <img src="uploads/user1avatar.png" />. Put the img tag on any page you want to show it to the user and populate it with that user’s avatar file path.

      Now, I’m not quite sure, but I think you might be asking specifically about how to display the posted image to the user on the same page (right after they post it asynchronously). To do that, you could have an empty img tag on the upload page, and then return the image’s file path from the POST call (and then set the img tag to use the file path).

      2. For the 415 error response, two things to check:
      a. Make sure to use content-type = multipart/form-data. Also, here’s an example of uploading a file with Postman.

      b. If you’re receiving the IFormFile as part of a model class (instead of as a method parameter), use the [FromForm] attribute on the model parameter. Check the IFormFile as a model property section for an example

      If you’re still running into problems, feel free to send a code snippet if you wish, and I’ll reply when I get a chance.

      Reply

Leave a Reply to Ibrahim Cancel reply