Navaneeth
Navaneeth

Reputation: 447

Download file from controller

In an Aspnet5 RC1 update1 web application, I am trying to do the same as Response.BinaryWrite, file download in old AspNet application.

The user needs to get a popup save dialog in client side.

When the following code is used, a popup prompt appears in client side to save the file:

public void Configure(IApplicationBuilder app)
{
    //app.UseIISPlatformHandler();
    //app.UseStaticFiles();
    //app.UseMvc();
    app.Run(async (objContext) =>
    {
        var cd = new System.Net.Mime.ContentDisposition { 
                     FileName = "test.rar", Inline = false };
        objContext.Response.Headers.Add("Content-Disposition", cd.ToString());
        byte[] arr = System.IO.File.ReadAllBytes("G:\\test.rar");
        await objContext.Response.Body.WriteAsync(arr, 0, arr.Length);
    });
}

But when the same code is used inside a Controller's action, app.Run is commented out, the save popup does not appear, the content is just rendered as text.

[Route("[controller]")]
public class SampleController : Controller
{
    [HttpPost("DownloadFile")]
    public void DownloadFile(string strData)
    {
        var cd = new System.Net.Mime.ContentDisposition { 
                     FileName = "test.rar", Inline = false };                
        Response.Headers.Add("Content-Disposition", cd.ToString());
        byte[] arr = System.IO.File.ReadAllBytes("G:\\test.rar");                
        Response.Body.WriteAsync(arr, 0, arr.Length);  

Control flow needs to come to controller, perform some logic, then send byte[] response content to client side, then the user needs to save the file. There is no cshtml, just plain html with jquery ajax call.

Upvotes: 5

Views: 14727

Answers (3)

Oleg
Oleg

Reputation: 221997

Do you really need HttpPost attribute? Try to remove it or to use HttpGet instead:

public async void DownloadFile()
{
    Response.Headers.Add("content-disposition", "attachment; filename=test.rar");
    byte[] arr = System.IO.File.ReadAllBytes(@"G:\test.rar");
    await Response.Body.WriteAsync(arr, 0, arr.Length);
}

UPDATED: Probably more easy would be the usage of FileStreamResult:

[HttpGet]
public FileStreamResult DownloadFile() {
    Response.Headers.Add("content-disposition", "attachment; filename=test.rar");
    return File(new FileStream(@"G:\test.rar", FileMode.Open),
                "application/octet-stream"); // or "application/x-rar-compressed"
}

Upvotes: 10

Ilya Chernomordik
Ilya Chernomordik

Reputation: 30175

I think the suggested answers with writing directly to the response are not quite correct, since the direct access to Response is not something yous should be thinking in the controller. There are better ways of doing it, like e.g. described here:

http://forums.asp.net/t/2011115.aspx?How+to+download+a+file+from+a+path+in+asp+net+vnext+

public class FileResult : ActionResult
{
    public FileResult(string fileDownloadName, string filePath, string contentType)
    {            
        FileDownloadName = fileDownloadName;
        FilePath = filePath;
        ContentType = contentType;
    }

    public string ContentType { get; private set; }
    public string FileDownloadName { get; private set; }
    public string FilePath { get; private set; }

    public async override Task ExecuteResultAsync(ActionContext context)
    {
        var response = context.HttpContext.Response;
        response.ContentType = ContentType;
        context.HttpContext.Response.Headers.Add("Content-Disposition", new[] { "attachment; filename=" + FileDownloadName });
        using (var fileStream = new FileStream(FilePath, FileMode.Open))
        {
            await fileStream.CopyToAsync(context.HttpContext.Response.Body);
        }
    }
}

The main idea is to return ActionResult and not to mess with the Response directly. As far as I remember there was already some class like that in previous ASP.NET, but it seems the new one lacks it at least for now. You just need to return the FileResult in your controller.

Upvotes: 0

erikscandola
erikscandola

Reputation: 2936

I alway use this block of code to download files and it works fine for me. I adapted it to your code.

public ActionResult DownloadFile(string strData) {
    var cd = new System.Net.Mime.ContentDisposition { FileName = "test.rar", Inline = false };
    byte[] arr = System.IO.File.ReadAllBytes(@"G:\test.rar");

    Response.ContentType = "application/x-rar-compressed";
    Response.AddHeader("content-disposition", cd.ToString());
    Response.Buffer = true;
    Response.Clear();
    Response.BinaryWrite(arr);
    Response.End();

    return new FileStreamResult(Response.OutputStream, Response.ContentType);
}

Upvotes: 0

Related Questions