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.
Let’s start it.
VIDEO: How to Return a File From a Web API.
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:
Now, let’s execute the images-stream
route:
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:
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.Â
what about memory leak ?
Hi, this is the official statement from Microsoft:
Of course, this is valid if your comment was related to this.
Any links highly appreciated …
Hi. If you think about that quote, this is the link:https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream?view=net-7.0#remarks
IFileService returns Stream not MemoryStream. It is very prone to errors when you just change the implementation. I think one extra word “using” does not make the example hard to understand.
Yes, but the implementation of the method works with MemoryStream. But ok, adding the using keyword won’t hurt anyone.
so FileStreamResultExecutor will make it but it is worth to mention that since it is not obvious.
Where’s this magical File() coming from?
Hi James. I am not sure why is it magical, but if you inspect our source code, which you can find at the top of each of our articles, you will find the method inside the controller: https://github.com/CodeMazeBlog/CodeMazeGuides/blob/main/aspnetcore-webapi/ReturnFileWebApi/ReturnFileWebApi/Controllers/DownloadsController.cs
As you can return NotFound, BadRequest, or OK from an action inside the controller, you can return File as well.
It’s magical because it only belongs to MVC not WebAPI controllers and it’s a function inherited not imported.
Well, James, you asked where this method is coming from and I just assumed that you don’t know (just by reading the article) how we use this method and how we access it. So, I gave you the simple answer. Now, if you go that deep into the Microsoft.AspNetCore.Mvc, you will find the ControllerBase class, which contains all the methods that I mentioned in my reply. Since each controller inherits that ControllerBase class, this is also part of the Web API project.
But at this point, I am not sure what was the point of your initial question.
Right. Sorry if I seemed frustrated; I was not; I said that with a smile. Everything you said is correct. This just does not apply to WebAPI users. You might make that clear to the readers, but I seem to be the only stupid one here. In another coincidence, there was an error in a middleware that we have which I did not write, but I see that someone else had the same error, so I assume both came from the same code sample that was copied. When I implemented the WebAPI version, the error presented itself, and I didn’t know which way was up or down. Answers to my question with hundreds of upvotes didn’t work for me until I fixed the issue with the middleware.
I couldn’t find the link to
“How to consume this API in client application”
Please help
I am not sure that I understand. What link are you referring to?
How retrieve image as part of json endpoint in webapi 2 asp.net?
Thanks for sharing.
see how to consume this service from angular.
From this moment I thank you for your answer.
You can read more about it here: https://code-maze.com/download-files-dot-net-core-angular/