1

I'm trying to download a file. It "seems" everything is ok, no exception is being thrown.

I am calling the action controller from the frontend with a simple jQuery $.ajax call.

What's wrong with my code?

MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("Line 1");
tw.WriteLine("Line 2");
tw.WriteLine("Line 3");
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();

Response.Clear();
Response.ContentType = "text/plain";
Response.AddHeader("Content-Disposition", "attachment; filename= YourFileName.txt");
Response.BinaryWrite(bytes);
Response.End();

return Json(resultado, JsonRequestBehavior.AllowGet);
Zysce
  • 1,200
  • 1
  • 10
  • 35
dlerma
  • 49
  • 1
  • 5
  • You're not going to be able to return JSON and download a file in the same HTTP request. That's not how HTTP works. And if you're in an MVC controller, don't write directly to the response. Instead, return an action result that contains the file data. – mason Sep 04 '18 at 18:57
  • Check https://stackoverflow.com/questions/12975886/how-to-download-a-file-using-web-api-in-asp-net-mvc-4-and-jquery – Zysce Sep 04 '18 at 18:58

2 Answers2

1

Change your code to this one and it should work.

MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);
tw.WriteLine("Line 1");
tw.WriteLine("Line 2");
tw.WriteLine("Line 3");
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();

return File(bytes, "text/plain", "YourFileName.txt");

if in some browser text file get opened automatically than you can try, "application/octet-stream" content type hack.. so, last line should be like,

return File(bytes, "application/octet-stream", "YourFileName.txt");
hsCode
  • 469
  • 4
  • 11
  • 1
    That will not work if OP is making an ajax call as they have stated. –  Sep 04 '18 at 22:25
0

First thing you should know: you cannot directly return a file from an AJAX callback, even it uses Response instance. However, you can return a string which contains file name, and store file contents as byte array inside TempData or Session:

[HttpPost]
public ActionResult ActionName(...)
{
    byte[] bytes;

    // other logic here

    using (var ms = new MemoryStream())
    {
        TextWriter tw = new StreamWriter(ms);
        tw.WriteLine("Line 1");
        tw.WriteLine("Line 2");
        tw.WriteLine("Line 3");
        tw.Flush();
        bytes = ms.ToArray();
    }

    TempData["bytes"] = bytes; // add this line

    return Json(new { fileName = "YourFileName.txt" });
}

Next, handle success part of AJAX call with window.location or window.location.href set to URL which points to action name which will download the file:

$.ajax({
    type: 'POST',
    url: '@Url.Action("ActionName", "ControllerName")',
    data: ...,
    success: function(result) {
        window.location = '@Url.Action("DownloadFile", "ControllerName")' + '?fileName=' + result.fileName; 
    }
});

Finally, create a controller with HTTP GET method and use byte array previously stored inside TempData/Session to return FileResult:

[HttpGet]
public FileResult DownloadFile(string fileName)
{
    if (TempData["bytes"] != null)
    {
        var content = TempData["bytes"] as byte[];
        return File(content, "application/octet-stream", fileName);
    }    
    else
    {
        return new EmptyResult();
    }
}

If you're doing all of those in correct way, the text file should be able to download.

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61