Return file in ASP.Net Core Web API
Solution 1
If this is ASP.net-Core then you are mixing web API versions. Have the action return a derived IActionResult
because in your current code the framework is treating HttpResponseMessage
as a model.
[Route("api/[controller]")]
public class DownloadController : Controller {
//GET api/download/12345abc
[HttpGet("{id}")]
public async Task<IActionResult> Download(string id) {
Stream stream = await {{__get_stream_based_on_id_here__}}
if(stream == null)
return NotFound(); // returns a NotFoundResult with Status404NotFound response.
return File(stream, "application/octet-stream"); // returns a FileStreamResult
}
}
Note:
The framework will dispose of the stream used in this case when the response is completed. If a
using
statement is used, the stream will be disposed before the response has been sent and result in an exception or corrupt response.
Solution 2
You can return FileResult with this methods:
1: Return FileStreamResult
[HttpGet("get-file-stream/{id}"]
public async Task<FileStreamResult> DownloadAsync(string id)
{
var fileName="myfileName.txt";
var mimeType="application/....";
Stream stream = await GetFileStreamById(id);
return new FileStreamResult(stream, mimeType)
{
FileDownloadName = fileName
};
}
2: Return FileContentResult
[HttpGet("get-file-content/{id}"]
public async Task<FileContentResult> DownloadAsync(string id)
{
var fileName="myfileName.txt";
var mimeType="application/....";
byte[] fileBytes = await GetFileBytesById(id);
return new FileContentResult(fileBytes, mimeType)
{
FileDownloadName = fileName
};
}
Solution 3
Here is a simplistic example of streaming a file:
using System.IO;
using Microsoft.AspNetCore.Mvc;
[HttpGet("{id}")]
public async Task<FileStreamResult> Download(int id)
{
var path = "<Get the file path using the ID>";
var stream = File.OpenRead(path);
return new FileStreamResult(stream, "application/octet-stream");
}
Note:
Be sure to use FileStreamResult
from Microsoft.AspNetCore.Mvc
and not from System.Web.Mvc
.
Solution 4
ASP.NET 5 WEB API & Angular 12
You can return a FileContentResult object (Blob) from the server. It'll not get automatically downloaded. You may create an anchor tag in your front-end app programmatically and set the href property to an object URL created from the Blob by the method below. Now clicking on the anchor will download the file. You can set a file name by setting the 'download' attribute to the anchor as well.
downloadFile(path: string): Observable<any> {
return this._httpClient.post(`${environment.ApiRoot}/accountVerification/downloadFile`, { path: path }, {
observe: 'response',
responseType: 'blob'
});
}
saveFile(path: string, fileName: string): void {
this._accountApprovalsService.downloadFile(path).pipe(
take(1)
).subscribe((resp) => {
let downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(resp.body);
downloadLink.setAttribute('download', fileName);
document.body.appendChild(downloadLink);
downloadLink.click();
downloadLink.remove();
});
}
Backend
[HttpPost]
[Authorize(Roles = "SystemAdmin, SystemUser")]
public async Task<IActionResult> DownloadFile(FilePath model)
{
if (ModelState.IsValid)
{
try
{
var fileName = System.IO.Path.GetFileName(model.Path);
var content = await System.IO.File.ReadAllBytesAsync(model.Path);
new FileExtensionContentTypeProvider()
.TryGetContentType(fileName, out string contentType);
return File(content, contentType, fileName);
}
catch
{
return BadRequest();
}
}
return BadRequest();
}
Related videos on Youtube
Jan Kruse
Updated on July 08, 2022Comments
-
Jan Kruse almost 2 years
Problem
I want to return a file in my ASP.Net Web API Controller, but all my approaches return the
HttpResponseMessage
as JSON.Code so far
public async Task<HttpResponseMessage> DownloadAsync(string id) { var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StreamContent({{__insert_stream_here__}}); response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); return response; }
When I call this endpoint in my browser, the Web API returns the
HttpResponseMessage
as JSON with the HTTP Content Header set toapplication/json
. -
Kurtis Jungersen over 5 yearsIn my case I needed to render an Excel in memory and return it for download, so I needed to define a file name with extension as well:
return File(stream, "application/octet-stream", "filename.xlsx");
This way when it downloads the user can open it directly. -
ΩmegaMan about 5 yearsI understand what the
NotFound()
ultimately does, but does it reside in .NET Core or is it something local to your project? -
Nkosi about 5 years@ΩmegaMan it is a helper method on
ControllerBase
and is part of the framework itself docs.microsoft.com/en-us/dotnet/api/… -
ΩmegaMan about 5 yearsOk, found my issue, though my controller was working in .NET Core 2.2, it was not derived from the base class
Controller
an thus didn't have access to theControllerBase.NotFound()
method. Once derived, it all worked. lol / thx -
Nkosi over 4 yearsIf within a
ControllerBase
there are many overloaded versions ofControllerBase.File
helper that returns any one of those. -
Nkosi over 4 yearsYour answer is still valid. So do not feel disheartened. I was just pointing out some resources you can use to support your answer.
-
Hamed Naeemaei about 4 yearsYes this is true.
-
user1066231 almost 4 years@Nkosi any way to transfer obtain file from url and stream it to client in chunks as we get from source? stackoverflow.com/questions/62476452/…
-
Nkosi almost 4 years@user1066231 have a look at this docs.microsoft.com/en-us/aspnet/core/release-notes/… focus on range header
-
Rob L over 3 yearsDo you not need a using statement here?
-
Nkosi over 3 years@RobL not in this case. the framework will dispose of the stream when the response is completed. If you use a
using
statement the stream will be disposed before the response has been sent. -
Nkosi over 3 years@RobL check the source code that actually writes the stream to the response. Note the
using
statement applied to the passed in stream source.dot.net/#Microsoft.AspNetCore.Mvc.Core/Infrastructure/… -
CoderSteve over 3 yearsWon't even compile because it can't convert the File(...) to Task<IActionResult>.
-
Nkosi over 3 years@CoderSteve did you await anything in the action?
-
Nkosi over 3 years@CoderSteve if you look at the source code You will see that
File
does return anIActionResult
derived object. -
Martin Schneider over 3 yearsThe magic behind
__get_stream_based_on_id_here__
could be interesting since common functions that return a Stream of a file are not async whereas functions that are async are only returning a byte array etc. Ofc, I could create another Stream from the byte array but I was wondering if there is a solution with one Stream only. -
Kevin Brydon over 3 yearsThank you @Nkosi, your comment saved my sanity! I was getting a 500 server error when returning a
File(...)
. I had the memoryStream incorrectly wrapped in a using statement. -
Nkosi over 3 years@KevinBrydon I am glad it helped. Happy Coding.
-
Vaibhav Deshmukh over 2 yearsIf someone trying in postman then you can also convert from stream to byte[] and then return byte[] in the return File(byte[]., mimetype) parameter
-
Mark Homer about 2 yearswhy would you pass a filepath from the frontend to the backend
-
Tanvir about 2 yearsAssume there is a page that lists uploaded user documents by file name, each list item(document) has a download button, the backend is WEB API.
-
Mark Homer about 2 yearsyou would pass a name not a path: path to upload, name or id to download
-
Tanvir about 2 yearsyeah, Id is the recommended field to pass. That code wasnt refactored.