In this article, we are going to learn how to return a file in an ASP.NET Core Web API and some concepts behind it. That said, we are going to implement a simple Web API to read a local file and return this file to download.

To download the source code for this article, you can visit our GitHub repository.

Let’s start it.

What is a MIME type?

Before we get to the code, we need to understand a couple of concepts to work with files on the internet. 

Once file extensions have no meaning for the web, the MIME Type (Multipurpose Internet Mail Extensions), is responsible for representing the nature and the format of a document we want to transmit over the internet.

The MIME Type structure consists of a type, a slash separator, and a subtype.

For this article, we are going to return a .png image, that said, we are going to use image/png as MIME Type to define our file.

It is good to mention that, application/octet-stream is a default MIME Type in case we need to return an unknown file.

To learn more about the most common MIME Types, check the Common MIME Types article. 

About Byte Array and Stream

In C#, we can represent binary data (a file, an image, or anything else stored on our computer) using byte[] or Stream instance.

A byte array (byte[]) is a simple array of bytes (unsigned 8-bit integer) containing the bytes of the file.

Stream is an abstract class to represent a sequence of bytes. The most common class that derives from Stream is FileStream and MemoryStream.   

You can check the Convert Byte Array to File in C# article to learn more about byte arrays and the C# Back to Basics – Files, StreamWriter and StreamReader to learn more about Stream. 

Web API Controller Preparation

Once we have an ASP.NET Core Web API ready, let’s create an empty API Controller:

[Route("api/[controller]")]
[ApiController]
public class DownloadsController : ControllerBase
{
    private readonly IFileService _fileService;
    private const string MimeType = "image/png";
    private const string FileName = "CM-Logo.png";

    public DownloadsController(IFileService fileService)
    {
        _fileService = fileService;
    }
}

Note that we have a _fileService, a MimeType, and a FileName field. The _fileService contains the logic to transform the file into a byte array or a stream. For this article, we are not going into detail about how to do it, however, you can check the FileService class implementation in our source code.

The MimeType field represents the nature of the file we want to return and the FileName represents a suggestion file name to download.

Return a File in ASP.NET Core Web API As a ByteArray

Now, let’s create our first ReturnByteArray action to return a file using Byte Array:

[HttpGet("images-byte")]
public IActionResult ReturnByteArray()
{
    var image = _fileService.GetImageAsByteArray();

    return File(image, MimeType, FileName);
}

Note that this is an images-byte route with the Get HTTP Verb. Once we are using the same file result for every request, we don’t need any input parameter.

So, first, we use GetImageAsByteArray() from the FileService class to retrieve the image we want to return as an array of bytes.

Then, we return a call to the File method passing the image file as a byte array, the mimeType we set as a class field, and the file name we want to return, as parameters.

The File method lives under the ControllerBase abstract class and returns a FileContentResult, which is responsible to provide the file to download.

Return a File in Web API As a Stream

Now, let’s create a ReturnStream action:

[HttpGet("images-stream")]
public IActionResult ReturnStream()
{
    var image = _fileService.GetImageAsStream();

    return File(image, MimeType, FileName);
}

We are going to keep the Get HTTP Verb, but this time, we set our route to images-stream.

This action is similar to the one in which we are using byte[] with the exception that, now, we are calling the GetImageAsStream() method from the FileService class.

Finally, we return a call to the File method. Note that this time we are passing a Stream object instead of a byte[] as the first parameter. This is possible because the File method has many overloads.

Downloading The File

Now that our code is ready, let’s check the behavior against when we are using swagger, postman, and the browser.

Using Swagger

Once we run the Web API application, we can see our two routes in Swagger:

/api/Downloads/images-byte
/api/Downloads/images-stream

Let’s call the images-byte route and check the result:

Image as a Byte Array

Now, let’s execute the images-stream route:

Image as a Stream

As we can see, in both cases we received an HTTP Status Code 200 as a result, indicating success and, as a response body, a link to download the image.

Checking the Result Using Postman

Let’s check the behavior when we make requests via Postman:

Postman result when we Return File in ASP.NET Core Web API

As we can see, different from Swagger, Postman doesn’t provide us a link or open the dialog box to download the image. Instead, it shows the image in its body response.

Browser

When we call any route (images-byte or images-stream) the browser downloads the image into the default download folder or opens the dialog box to indicate which folder we want to save the file.

Conclusion

In this article, besides learning some concepts behind how the web handles files, we have seen how to return a file using Asp.Net Core Web API. Also, we have checked how swagger, postman, and browser behave when we request a file in a Web API.