C# ASP.NET MVC Download a File Via Ajax

Download Excel file via AJAX MVC

You can't directly return a file for download via an AJAX call so, an alternative approach is to to use an AJAX call to post the related data to your server. You can then use server side code to create the Excel File (I would recommend using EPPlus or NPOI for this although it sounds as if you have this part working).

UPDATE September 2016

My original answer (below) was over 3 years old, so I thought I would update as I no longer create files on the server when downloading files via AJAX however, I have left the original answer as it may be of some use still depending on your specific requirements.

A common scenario in my MVC applications is reporting via a web page that has some user configured report parameters (Date Ranges, Filters etc.). When the user has specified the parameters they post them to the server, the report is generated (say for example an Excel file as output) and then I store the resulting file as a byte array in the TempData bucket with a unique reference. This reference is passed back as a Json Result to my AJAX function that subsequently redirects to separate controller action to extract the data from TempData and download to the end users browser.

To give this more detail, assuming you have a MVC View that has a form bound to a Model class, lets call the Model ReportVM.

First, a controller action is required to receive the posted model, an example would be:

public ActionResult PostReportPartial(ReportVM model){

// Validate the Model is correct and contains valid data
// Generate your report output based on the model parameters
// This can be an Excel, PDF, Word file - whatever you need.

// As an example lets assume we've generated an EPPlus ExcelPackage

ExcelPackage workbook = new ExcelPackage();
// Do something to populate your workbook

// Generate a new unique identifier against which the file can be stored
string handle = Guid.NewGuid().ToString();

using(MemoryStream memoryStream = new MemoryStream()){
workbook.SaveAs(memoryStream);
memoryStream.Position = 0;
TempData[handle] = memoryStream.ToArray();
}

// Note we are returning a filename as well as the handle
return new JsonResult() {
Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
};

}

The AJAX call that posts my MVC form to the above controller and receives the response looks like this:

$ajax({
cache: false,
url: '/Report/PostReportPartial',
data: _form.serialize(),
success: function (data){
var response = JSON.parse(data);
window.location = '/Report/Download?fileGuid=' + response.FileGuid
+ '&filename=' + response.FileName;
}
})

The controller action to handle the downloading of the file:

[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{
if(TempData[fileGuid] != null){
byte[] data = TempData[fileGuid] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else{
// Problem - Log the error, generate a blank file,
// redirect to another controller action - whatever fits with your application
return new EmptyResult();
}
}

One other change that could easily be accommodated if required is to pass the MIME Type of the file as a third parameter so that the one Controller action could correctly serve a variety of output file formats.

This removes any need for any physical files to created and stored on the server, so no housekeeping routines required and once again this is seamless to the end user.

Note, the advantage of using TempData rather than Session is that once TempData is read the data is cleared so it will be more efficient in terms of memory usage if you have a high volume of file requests. See TempData Best Practice.

ORIGINAL Answer

You can't directly return a file for download via an AJAX call so, an alternative approach is to to use an AJAX call to post the related data to your server. You can then use server side code to create the Excel File (I would recommend using EPPlus or NPOI for this although it sounds as if you have this part working).

Once the file has been created on the server pass back the path to the file (or just the filename) as the return value to your AJAX call and then set the JavaScript window.location to this URL which will prompt the browser to download the file.

From the end users perspective, the file download operation is seamless as they never leave the page on which the request originates.

Below is a simple contrived example of an ajax call to achieve this:

$.ajax({
type: 'POST',
url: '/Reports/ExportMyData',
data: '{ "dataprop1": "test", "dataprop2" : "test2" }',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function (returnValue) {
window.location = '/Reports/Download?file=' + returnValue;
}
});
  • url parameter is the Controller/Action method where your code will create the Excel file.
  • data parameter contains the json data that would be extracted from the form.
  • returnValue would be the file name of your newly created Excel file.
  • The window.location command redirects to the Controller/Action method that actually returns your file for download.

A sample controller method for the Download action would be:

[HttpGet]
public virtual ActionResult Download(string file)
{
string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
return File(fullPath, "application/vnd.ms-excel", file);
}

How to implement file download with AJAX and MVC

What I ended up doing is calling my controller from my javascript like:

var url = "/mycontroller/GetFile?startDate=" + $("#mydate").val() + etc...

window.location = url;

mycontroller.cs

 public void GetFile(DateTime startDate) 
{

}

My original concern was with the date parameters. I didnt want to have to parse it.

How to download a file through ajax request in asp.net MVC 4

Please, try this in ajax success

success: function () {
window.location = '@Url.Action("DownloadAttachment", "PostDetail")';
}

Updated answer:

public ActionResult DownloadAttachment(int studentId)
{
// Find user by passed id
// Student student = db.Students.FirstOrDefault(s => s.Id == studentId);

var file = db.EmailAttachmentReceived.FirstOrDefault(x => x.LisaId == studentId);

byte[] fileBytes = System.IO.File.ReadAllBytes(file.Filepath);

return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, file.Filename);

}

Ajax request:

$(function () {
$("#DownloadAttachment").click(function () {
$.ajax(
{
url: '@Url.Action("DownloadAttachment", "PostDetail")',
contentType: 'application/json; charset=utf-8',
datatype: 'json',
data: {
studentId: 123
},
type: "GET",
success: function () {
window.location = '@Url.Action("DownloadAttachment", "PostDetail", new { studentId = 123 })';
}
});

});
});

Trying to download Zip file using ajax but can't able to download in asp.net mvc

1 In your controller or razor age

  • a. Remove the using in your controller
  • b. No need to call .ToArray() on the stream
public IActionResult DownloadFile(//params)
{
var memoryStram = //wherever it is you are getting your stream from

zip.Save(memoryStream);

memoryStream.Position = 0; // i tend to set this if i dont know the state of the stream

return File(memoryStream, "application/zip", zipName);
}

2 Your ajax call

Option 1:
Make an ajax call and then redirect once completed to get the file

function download(e) {
$.ajax({
type: "POST",
url: '/url', //call your controller and action
contentType: "application/json; charset=utf-8",
dataType: "json", // dont forget this is required
}).done(function () {
//use window.location.href for redirect to download action for download the file
window.location.href = '/api/AjaxAPI/Download';
});
}

Option 2: Make it all in one go

$.ajax({
url: '/url', // url to your controller/action
method: 'GET',
contentType: "application/json; charset=utf-8",
dataType: "json", // dont forget this is required
success: function (data) {
var a = document.createElement('a');
var blob = new Blob([data], {type: "application/zip"})
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = 'myfile.zip';
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
}
});

These are the 2 I can think of off the top of my head., i am sure there are more.

Download a file through ajax request in asp.net MVC

You should know that AJAX call is not intended to download CSV file directly. Therefore, you can create a byte array from MemoryStream instance and store it inside Session or TempData variable, then return 'successful' state to enable redirect on AJAX success response:

public ActionResult GetHistoricalUsageApplicationFile([DataSourceRequest]DataSourceRequest request, [FromBody] string accountCodes)
{
var HistoricalUsagesData = _enrollmentManagementRepository.GetHistoricalUsageApplicationFile(accountCodes);
List<HistoricalUsageApplicationFileModel> HUApplications = _mapper.MapToNew<List<HistoricalUsageApplicationFileModel>>(HistoricalUsagesData);
//var HistoricalUsageApplication = HUReport.ToDataSourceResult(request).Data;

var output = new MemoryStream();
var writer = new StreamWriter(output, Encoding.UTF8);

writer.Write("CommodityCode,");
writer.Write("CustomerTypeCode,");
writer.Write("EnrollmentRequestId");
writer.WriteLine();

var list = HUApplications.ConvertToString();
var single = list.Aggregate((x, y) => { return string.Concat(x, y); });

writer.WriteAsync(single);
writer.Flush();
output.Position = 0;

// creates byte array from stream
TempData["Output"] = output.ToArray();

// returns successful state
return Json("Success", JsonRequestBehavior.AllowGet);
}

Second, create a controller action with GET method and pass stored byte array from Session or TempData into FileResult:

public ActionResult DownloadCSV()
{
// retrieve byte array here
var array = TempData["Output"] as byte[];
if (array != null)
{
return File(array, System.Net.Mime.MediaTypeNames.Application.Octet, "Products.csv");
}
else
{
return new EmptyResult();
}
}

Finally, handle success response to include location.href which will redirect to controller returning FileResult to download CSV file:

$.ajax({
url: '@Url.Action("GetHistoricalUsageApplicationFile", "HUProducts")',
type: 'GET',
data: { "accountCodes": strAccountCodes },
success: function (result) {
if (result == "Success") {
location.href = '@Url.Action("DownloadCSV", "ControllerName")';
}
}
});

As an option, you could pass CSV file name as parameter from AJAX response using query string.

Related issue:

Creating a byte array from a stream



Related Topics



Leave a reply



Submit