86

In MVC, we have used the following code to download a file. In ASP.NET core, how to achieve this?

    HttpResponse response = HttpContext.Current.Response;                 
    System.Net.WebClient net = new System.Net.WebClient();
    string link = path;
    response.ClearHeaders();
    response.Clear();
    response.Expires = 0;
    response.Buffer = true;
    response.AddHeader("Content-Disposition", "Attachment;FileName=a");
    response.ContentType = "APPLICATION/octet-stream";
    response.BinaryWrite(net.DownloadData(link));
    response.End();
Askolein
  • 3,250
  • 3
  • 28
  • 40
BALA MURUGAN
  • 1,825
  • 3
  • 16
  • 17

10 Answers10

85

Your controller should return an IActionResult, and use the File method, such as this:

[HttpGet("download")]
public IActionResult GetBlobDownload([FromQuery] string link)
{
    var net = new System.Net.WebClient();
    var data = net.DownloadData(link);
    var content = new System.IO.MemoryStream(data);
    var contentType = "APPLICATION/octet-stream";
    var fileName = "something.bin";
    return File(content, contentType, fileName);
}
Métoule
  • 13,062
  • 2
  • 56
  • 84
  • 1
    I´m receiving an HTTP 500 from the application, but if I save the file to disk, it performs ok (but still http 500 ) It´s an PDF file... Any Thoughts? – Daniel Feb 11 '19 at 05:05
  • 1
    @Daniel HTTP 500 means literaly "Anything", so since the server doesn't know what happened, it just says "Internal Server Error". The only way to solve this problem is by finding out the real error, which often is unrelated to business logic, much probably being a programming error like NullReferenceException or conversion between incompatible types, etc. Log files and debugging are some valid ways to troubleshot this kind of issue. – Ulysses Alves May 07 '19 at 13:26
  • 1
    I am downloading excel file. It downloads but when I try to open the file it says the file is corrupted. even the size of the file which is on server and which I downloaded is not equal. it is 2.7kb on server. But when I download it is 10kb. What kind of problem is this? – Abdulhakim Zeinu Feb 05 '20 at 15:15
  • 3
    This is an old answer, but my comment still applies. This code leaks a MemoryStream. The MemoryStream should be nested in a using block so that managed resources can be disposed. If he using is added, the method can't return the File because the MemoryStream goes out of scope and is disposed before the file is returned. – Kevingy Jun 05 '20 at 01:36
  • 6
    The ASP.NET Core infrastructure takes care of disposing the stream when sending the response, so there's no leak (see the `FileResultExecutorBase.WriteFileAsync` method). If you try to surround this code with a using statement, no file will be sent. – Métoule Jun 05 '20 at 06:55
36

You can try below code to download the file. It should return the FileResult

public ActionResult DownloadDocument()
{
string filePath = "your file path";
string fileName = "your file name";
    
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
    
return File(fileBytes, "application/force-download", fileName);
    
}
JacobIRR
  • 8,545
  • 8
  • 39
  • 68
XamDev
  • 3,377
  • 12
  • 58
  • 97
28

A relatively easy way to achieve this is to use the built-in PhysicalFile result, which is available to all controllers: MS Docs: PhysicalFile

A simple example:

public IActionResult DownloadFile(string filePath)
{
     return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath), Path.GetFileName(filePath));
}

Now of course you should never expose this kind of API, due to security concerns.

I typically shield the actual file paths behind a friendly identifier, which I then use to lookup the real file path (or return a 404 if an invalid ID was passed in), i.e.:

[HttpGet("download-file/{fileId}")]
public IActionResult DownloadFile(int fileId)
{
    var filePath = GetFilePathFromId(fileId);
    if (filePath == null) return NotFound();
        
    return PhysicalFile(filePath, MimeTypes.GetMimeType(filePath), Path.GetFileName(filePath));
}

For those that are curious, the MimeTypes helper is a great little Nuget package from the folks at MimeKit

Amal K
  • 4,359
  • 2
  • 22
  • 44
RMD
  • 2,907
  • 30
  • 47
13

Here is my Medium article, describing everything step by step (it also includes a GitHub repo): https://medium.com/@tsafadi/download-a-file-with-asp-net-core-e23e8b198f74

Any ways this is how the controller should look like:

[HttpGet]
public IActionResult DownloadFile()
{
    // Since this is just and example, I am using a local file located inside wwwroot
    // Afterwards file is converted into a stream
    var path = Path.Combine(_hostingEnvironment.WebRootPath, "Sample.xlsx");
    var fs = new FileStream(path, FileMode.Open);

    // Return the file. A byte array can also be used instead of a stream
    return File(fs, "application/octet-stream", "Sample.xlsx");
}

Inside the view:

$("button").click(function () {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "Download/DownloadFile", true);
    xhr.responseType = "blob";
    xhr.onload = function (e) {
        if (this.status == 200) {
            var blob = this.response;

            /* Get filename from Content-Disposition header */
            var filename = "";
            var disposition = xhr.getResponseHeader('Content-Disposition');
            if (disposition && disposition.indexOf('attachment') !== -1) {
                var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                var matches = filenameRegex.exec(disposition);
                if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
            }

            // This does the trick
            var a = document.createElement('a');
            a.href = window.URL.createObjectURL(blob);
            a.download = filename;
            a.dispatchEvent(new MouseEvent('click'));
        }
    }
    xhr.send();
});
Tom el Safadi
  • 6,164
  • 5
  • 49
  • 102
8

Create a Service say FileService.

public class FileService
{
    private readonly IHostingEnvironment _hostingEnvironment;
    constructor(IHostingEnvironment hostingEnvironment)
    {
        this._hostingEnvironment = hostingEnvironment;
    }
}

Add a method to FileService MimeType of the file

private string GetMimeType(string fileName)
{
    // Make Sure Microsoft.AspNetCore.StaticFiles Nuget Package is installed
    var provider = new FileExtensionContentTypeProvider();
    string contentType;
    if (!provider.TryGetContentType(fileName, out contentType))
    {
        contentType = "application/octet-stream";
    }
    return contentType;
}

Now add a method to download File,

public FileContentResult GetFile(string filename) 
{
    var filepath = Path.Combine($"{this._environment.WebRootPath}\\path-to-required-folder\\{filename}");

    var mimeType = this.GetMimeType(filename); 

    byte[] fileBytes;

    if (File.Exists(filepath))
    {
        fileBytes = File.ReadAllBytes(filepath); 
    } 
    else
    {
        // Code to handle if file is not present
    }

    return new FileContentResult(fileBytes, mimeType)
    {
        FileDownloadName = filename
    };
}

Now add controller method and call GetFile method in FileService,

 public IActionResult DownloadFile(string filename) 
 {
    // call GetFile Method in service and return       
 }
20B2
  • 2,011
  • 17
  • 30
7

Example for Asp.net Core 2.1+ (Best practice)

Startup.cs:

private readonly IHostingEnvironment _env;

public Startup(IConfiguration configuration, IHostingEnvironment env)
{
    Configuration = configuration;
    _env = env;
}

services.AddSingleton(_env.ContentRootFileProvider); //Inject IFileProvider

SomeService.cs:

private readonly IFileProvider _fileProvider;

public SomeService(IFileProvider fileProvider)
{
    _fileProvider = fileProvider;
}

public FileStreamResult GetFileAsStream()
{
    var stream = _fileProvider
        .GetFileInfo("RELATIVE PATH TO FILE FROM APP ROOT")
        .CreateReadStream();

    return new FileStreamResult(stream, "CONTENT-TYPE")
}

Controller will return IActionResult

[HttpGet]
public IActionResult Get()
{
    return _someService.GetFileAsStream() ?? (IActionResult)NotFound();
}
Dmitry
  • 1,095
  • 14
  • 21
5

Action method needs to return FileResult with either a stream, byte[], or virtual path of the file. You will also need to know the content-type of the file being downloaded. Here is a sample (quick/dirty) utility method. Sample video link How to download files using asp.net core

[Route("api/[controller]")]
public class DownloadController : Controller
{
    [HttpGet]
    public async Task<IActionResult> Download()
    {
        var path = @"C:\Vetrivel\winforms.png";
        var memory = new MemoryStream();
        using (var stream = new FileStream(path, FileMode.Open))
        {
            await stream.CopyToAsync(memory);
        }
        memory.Position = 0;
        var ext = Path.GetExtension(path).ToLowerInvariant();
        return File(memory, GetMimeTypes()[ext], Path.GetFileName(path));
    }

    private Dictionary<string, string> GetMimeTypes()
    {
        return new Dictionary<string, string>
        {
            {".txt", "text/plain"},
            {".pdf", "application/pdf"},
            {".doc", "application/vnd.ms-word"},
            {".docx", "application/vnd.ms-word"},
            {".png", "image/png"},
            {".jpg", "image/jpeg"},
            ...
        };
    }
}
VETRIVEL D
  • 91
  • 1
  • 3
2
    [HttpGet]
    public async Task<FileStreamResult> Download(string url, string name, string contentType)
    {
        var stream = await new HttpClient().GetStreamAsync(url);

        return new FileStreamResult(stream, contentType)
        {
            FileDownloadName = name,
        };
    }
Toprak
  • 559
  • 6
  • 10
0

my way is quite short and I think it suits most people's need.

  [HttpPost]
  public ActionResult Download(string filePath, string fileName)
  {
      var fileBytes = System.IO.File.ReadAllBytes(filePath);
      new FileExtensionContentTypeProvider().TryGetContentType(Path.GetFileName(filePath), out var contentType);
      return File(fileBytes, contentType ?? "application/octet-stream", fileName);
  }
Tim Gerhard
  • 3,477
  • 2
  • 19
  • 40
0

This worked for me :

 httpContext.Response.Headers.Append("content-disposition", "attachment;filename=" + mytextfilename);            
 httpContext.Response.ContentType = "application/text";
 httpContext.Response.WriteAsync(mytextfile);