jquery post not allowing csv file to download in MVC application - asp.net-mvc

I have a MVC application that has a Jquery Post
$.post(virtualPath + cookie + this.pageName + '/FunctionA/', parameters,function (filedata) {
alert(filedata);
},'application/csv');
}
this post is called from Javascript Event that is triggered by a buttonclick to download the file
I get the Server Side Http File Response
in the alert but cannot get it downloadable in the browser
The controller returns the Response as FileContentResult
[AcceptVerbs(HttpVerbs.Post)]
public FileContentResult FunctionA(string A, DateTime B)
{
try
{
string csv = "Make it downloadable ";
var filresult = File(new System.Text.UTF8Encoding().GetBytes(csv), "application/csv", "downloaddocuments.csv");
// return filresult;
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/vnd.ms-excel";
Response.AddHeader("content-disposition", "attachment; filename=Statement_" + "Downloadfile" + ".csv");
Response.Write(csv);
Response.Flush();
return filresult;
}
}

You cannot use AJAX to download files. The reason for that is because once the download succeeds and the success callback is invoked you cannot neither save the file automatically to the client browser nor you can prompt for the Save As dialog.
So instead of using javascript and AJAX to download this file simply use a standard link to the controller action which will allow the user to directly download the file.
UPDATE:
As requested in the comments section here's an example using an anchor:
#Html.ActionLink(
"download file",
"actionName",
"controllerName",
new {
param1 = "value1",
param2 = "value2",
},
null
)
or if you need to pass lots of parameters you might prefer to use a form with hidden fields that will POST:
#using (Html.BeginForm("actionName", "controllerName"))
{
#Html.Hidden("param1", "value1")
#Html.Hidden("param2", "value2")
<button type="submit">Download file</button>
}

Related

File download in aspx and MVC mixed mode case

The below given codes works perfect for me when i am using it with pure asp.net(aspx) application having redirection and front end in asp net.
Now my scenario is, i have mixed mode application in mvc and asp.net(aspx page)
so i am simply using same page(aspx) for downloading file by calling it from mvc controller method, which is not working .
i have written ajax call in view to call a void method in controller which redirects to aspx page where this code is written , rest of case is as mentioned above.
**string path = HttpContext.Current.Server.MapPath("~/Reporting/OnePager.xlsm");
string id = Request.Params["id"];
HttpResponse response = this.Response;
response.Buffer = true;
response.Clear();
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("content-disposition", "attachment; filename=" + id + "-One-Pager.xlsm");
response.WriteFile(path);
response.Flush();
response.End();**
Here is ajax call
if (arrselected.length > 0) {
$.ajax({
type: 'POST',
url: '#Url.Action("ExportProjectOnePager", "controller")',
dataType: 'json',
contentType: "application/json; charset=utf-8",
data: JSON.stringify({ ID: arrselected[0]}),
success: function (output) {
if (output.notValid.length != arrselected.length) {
alert("success");
}
if (output.notValid != "") {
alert("You do not have permission to see this IDs :" + output.notValid);
}
}
});
}
and here is void controller method
public void ExportProjectOnePager(string ID)
{
string _Path = #"~/Reporting/ProjectOnePager.aspx?id=" + ID;
Response.Redirect(_Path);
}
You can do the same in the controller method, but you have to change it to return FileResult or FileStreamResult, and copy the code there.
You can also change the controller method to return RedirectResult and inside the code use return Redirect('the aspx page') instead of Response.Redirect and return type is void

How to download a file to client from server?

I have an MVC project where I'd like the user to be able to download a an excel file with a click of a button. I have the path for the file, and I can't seem to find my answer through google.
I'd like to be able to do this with a simple button I have on my cshtml page:
<button>Button 1</button>
How can I do this? Any help is greatly appreciated!
If the file is not located inside your application folders and not accessible directly from the client you could have a controller action that will stream the file contents to the client. This could be achieved by returning a FileResult from your controller action using the File method:
public ActionResult Download()
{
string file = #"c:\someFolder\foo.xlsx";
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
return File(file, contentType, Path.GetFileName(file));
}
and then replace your button with an anchor pointing to this controller action:
#Html.ActionLink("Button 1", "Download", "SomeController")
Alternatively to using an anchor you could also use an html form:
#using (Html.BeginForm("Download", "SomeController", FormMethod.Post))
{
<button type="submit">Button 1</button>
}
If the file is located inside some non-accessible from the client folder of your application such as App_Data you could use the MapPath method to construct the full physical path to this file using a relative path:
string file = HostingEnvironment.MapPath("~/App_Data/foo.xlsx");
HTML:
<div>#Html.ActionLink("UI Text", "function_name", "Contoller_name", new { parameterName = parameter_value },null) </div>
Controller:
public FileResult download(string filename) {
string path = "";
var content_type = "";
path = Path.Combine("D:\file1", filename);
if (filename.Contains(".pdf"))
{
content_type = "application/pdf";
}
return File(path, content_type, filename);
}

ASP.NET MVC - Execute controller action without redirecting

I am trying to execute an action on a controller without redirecting to the associated view for that action. For a good example of what I am trying to achieve take a look at the music.xbox.com website. When you add a song to a selected playlist from a popup menu - the page just shows a notification without any redirect or refresh. how is this possible?
What I have is the following:
I have a _playlistPopupMenu partial view that renders the list of playlists as follows:
_PlaylistPopupMenu
#model List<OneMusic.Models.GetPlaylists_Result>
#if (Model.Count > 0)
{
<li style="height:2px" class="divider"></li>
foreach (var item in Model)
{
<li style="height:30px">#Html.DisplayFor(p => item.Name)
#Html.ActionLink(item.Name, "AddSong", "Playlist", new { playlistId = #item.PlaylistId, songId = 1 }, "")
</li>
}
}
The PlaylistController AddSong action is as follows:
public PartialViewResult AddSong(int? playlistId, int? songId)
{
if (ModelState.IsValid)
{
db.AddSongToPlaylist(playlistId, songId);
db.SaveChanges();
return PartialView("_AddToPlaylist", "");
}
return PartialView("_AddToPlaylist", "");
}
I am struggling with what to put in the _AddToPlaylist partial view which I think I need to be able to display a notification of some kind (Possiblly using PNotify add in for Bootstrap). MVC wants to always redirect to ../Playlist/AddSong?playlistId=1&songId=1
Any ideas on how to complete this last part of the problem would be great.
If you don't want "full page reloads" then you need to approach the problem slightly differently, using javascript to alter the page dynamically. A library such as JQuery might make manipulating the DOM a little easier.
Display the popup dynamically using javascript.
When the user hits OK/Submit on the popup, post the data back to the server using javascript, and have the controller you are posting to return some HTML.
Append the returned HTML block (partial view) to an existing div containing playlist tracks.
The most difficult part of this is the asynchronous post. Help with updating a div without reloading the whole page can be found in this question.
EDIT - Example
If you have a controller action (accepting POSTs) with the URL myapp.com/PlayList/AddSong/, then you'd set up JQuery to post to this URL. You'd also set up the data property with any form data which you'd like to post, in your case you'd add playistId and songId to the data property.
You'd then use the result of the AJAX query (HTML) and append it to the existing playlist HTML on the page. So assuming that you want to append the partial view's HTML to a div with ID playlistDiv, and assuming that your partial view returns HTML which is valid when appended to the existing playlist, then your javascript will look something like this:
var data = { playlistId: 1, songId: 1 };
$.ajax({
type: "POST",
url: 'http://myapp.com/PlayList/AddSong/',
data: data,
success: function(resultData) {
// take the result data and update the div
$("#playlistDiv").append(resultData.html)
},
dataType: dataType
});
Disclaimer: I can't guarantee that this code will work 100% (unless I write the program myself). There may be differences in the version of JQuery that you use, etc, but with a little tweaking it should achieve the desired result.
using System.Web.Mvc;
using System.Web.Mvc.Html;
public ActionResult Index()
{
HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new WebFormView(ControllerContext, "Index"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
helper.RenderAction("Index2");
return View();
}
public ActionResult Index2(/*your arg*/)
{
//your code
return new EmptyResult();
}
in your controller you must add bottom code:
public ActionResult Index(string msg)
{
if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com"))
{
string html = "";
using (System.Net.WebClient client = new System.Net.WebClient())
{
client.Encoding = Encoding.UTF8;
html = client.DownloadString("https://NewExampleUrl.com/first/index?id=1");
}
Response.Write(html);
}
...
}
your view must be empty so you add bottom code
#{
ViewBag.Title = "sample title";
if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com"))
{
Layout = null;
}else
{
Layout ="~/Views/Shared/_Layout.cshtml"
}
}
#if (Request.Url.ToString().Contains("yourNewExampleUrlWithOutRedirect.com")==false)
{
before view like :
<div>hello world</div>
}

Redirect / show view after generated file is dowloaded

I've got a controller action that downloads a dynamically generated file:
public ActionResult DownloadFile()
{
var obj = new MyClass { MyString = "Hello", MyBool = true };
var ser = new XmlSerializer(typeof(MyClass));
var stream = new MemoryStream();
ser.Serialize(stream, obj);
stream.Position = 0;
Response.Clear();
Response.AddHeader("Content-Disposition", "attachment; filename=myfile.xml");
Response.ContentType = "application/xml";
// Write all my data
stream.WriteTo(Response.OutputStream);
Response.End();
return Content("Downloaded");
}
Just for reference:
public class MyClass
{
public string MyString { get; set; }
public int MyInt { get; set; }
}
This is working, and the file (myfile.xml) is downloaded.
However, the message "Downloaded" is not sent to the browser.
Similarly, if I replace return Content("Downloaded");
for return Redirect("www.something.com");
then the browser is redirected before the file downloads.
As a bit of a pre-amble, the user journey is:
User fills out form on previous view
Form is submitted
XML is generated and downloaded
User is redirected / "Downloaded" view is shown (so hitting F5 won't re-post the form)
As Ross has said, you can only return one response to a HTTP request.
What i do in that case is:
Send the request to the server
The server generates the file and stores it in some server side data structure (Cache, Usersession, TempData)
The server returns a RedirectToAction() (POST, REDIRECT, GET pattern)
The redirected action returns a View with some javascript which
Triggers the download of the pregenerated file by setting window.location.href property to an special download action which sends the file back to the browser
Each HTTP request can only have one response - you're trying to sneak in two (the file, and a page).
Normally when you send a "Content-Disposition: attachment" HTTP header the browser will stay on the current page and pop a file save dialog (or automatically save the file in your downloads).
You're going to have to change your strategy if you want to prevent re-submission of the form. I'd suggest a bit of javascript to disable the form's submit button and show the "Completed" message in a div overlay?
Here is how I redirected after the file is downloaded.
The main logic is to wait the redirect until the file is downloaded.
To do that, a server side response is calculated and redirect is delayed using server side response time + some offset.
Server Side Controller Code:
[HttpPost]
public ActionResult GetTemplate()
{
return Json(new {Url = Url.Action("ReturnTemplate") });
}
[HttpGet]
public ActionResult ReturnTemplate()
{
FileResult fileResult = // your file path ;
return fileResult;
}
Client Side Code:
<div id="btnGen" align="right"><button class="main-button" id="generateTemplate" type="Submit"></div>
Javascript:
$("#generateTemplate").click(function () {
var startTime = (new Date()).getTime(), endTime;
$.ajax({
url: '#Url.Action("GetTemplate", "Controller")',
type: 'POST',
traditional: true,
dataType: "json",
contentType: "application/json",
cache: false,
data: JSON.stringify(),
success: function (result) {
endTime = (new Date()).getTime();
var serverResponseTime = endTime - startTime + 500;
setInterval(function () { Back() }, serverResponseTime);
window.location = result.Url;
}
});
});
function Back() {
window.location = '#Url.Action("Index","Controller")';
}

return PDF in ajax request

I have got an ajax request to my Server where i am creating an PDF File. Now i want to display this file in a new window/tab or just download it. how can i do that?
my request
$.ajax({
url: '/Document/CreatePDF',
type: 'POST',
data: {
docid: documentId,
dataId: array
},
traditional: true,
success: function (data) {
}
});
[HttpPost]
public FileStreamResult CreatePDF(long docid, List<long> dataId)
{
var document = _rep.LoadDocument(docid.ToString(), Server.MapPath("~/Documents/") + docid + ".xml");
var exporter = new PDFExporter(document);
MemoryStream fileStream = exporter.CreatePDF();
byte[] PdfByte = fileStream.GetBuffer();
fileStream.Flush();
fileStream.Close();
HttpContext.Response.AddHeader("content-disposition","attachment; filename=form.pdf");
return new FileStreamResult(fileStream, "application/pdf");
}
You cannot use AJAX to download files. The reason for that is because javascript doesn't allow you to save the downloaded content on the client computer, nor to prompt for a Save As dialog. You should use a simple HTML <form> or an anchor:
#using (Html.BeginForm("CreatePDF", "Document", FormMethod.Post, new { id = "myform" }))
{
<button type="submit">Download</button>
}
If you need to pass arguments to this controller action that are known only at the client you could subscribe to the .submit event of this form and then dynamically inject hidden fields into it with the corresponding values and then leave the default action execute. And if the values are known at the server side you should simply use HTML helpers to generate those hidden fields.

Resources