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.

Comments are closed.