I'm pretty sure this isn't possible but I thought I'd ask...
I have a FileResult which returns a file, when it works there's no problem. When there's an error in the FileResult for some reason, I'd like to display an exception error message on the users screen, preferably in a popup.
Can I do an Ajax post which returns a file when successful and displays a message if not?
I think it is not possible cause in order to handle ajax post, you will have to write a javascript handler on the client side and javascript cannot do file IO on client side.
However, what you can do is, make an ajax request to check if file exists and can be downloaded. If, not, respond to that request negatively which will popup a dialog on client side. If successful, make a file download request.
Not specifically related to MVC but...
it can be done using XMLHttpRequest in conjunction with the new HTML5 File System API that allows you to deal with binary data (fetched from http response in your case) and save it to the local file system.
See example here: http://www.html5rocks.com/en/tutorials/file/xhr2/#toc-example-savingimages
Controller (MyApiController) Code:
public ActionResult DownloadFile(String FileUniqueName)
{
var rootPath = Server.MapPath("~/UploadedFiles");
var fileFullPath = System.IO.Path.Combine(rootPath,FileUniqueName);
byte[] fileBytes = System.IO.File.ReadAllBytes(fileFullPath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, "MyDownloadFile");
}
Jquery Code:
$(document).on("click"," a ", function(){ //on click of anchor tag
var funame=$(this).attr('uname'); /*anchor tag attribute "uname" contain file unique name*/
var url = "http://localhost:14211/MyApi/DownloadFile?FileUniqueName= " + funame;
window.open(url);
});
Related
I realize that variations on this question have been asked before. The best answer I have found is at
File download in Asp.Net MVC 2
But trying to follow those instructions did not solve the problem for me.
Long story short, the file is being retrieved correctly, the name, path and mime type are all correct, and no errors are thrown. No errors are thrown by the javascript on the client-side either.
The C# code that gets the file looks like this:
[HttpPost]
public FileResult DownloadFile(int fileId)
{
... get the file and file info
return File(fileBytes, fileMimeType, fileName);
}
The javascript looks like this:
... set up for post here
$.post(settings.actions.downloadFile, {fileId: fileIdVar});
As I was saying, the post returns and nothing happens.
I have tried changing the post to a get, and the result was the same.
I have tried setting up a callback function that sets document.location.href to some random url on return from the download, but that just takes my browser to the page I specified. I cannot understand, from the explanation given in the link I provided, that is
"...Use document.location.href = ... to tell the browser to go to the url for downloading the file. It'll see the content disposition header and will display it as a download not as a page..."
What I'm supposed to point my browser to. document.location.href doesn't accept data, so I can't use it on its own, and using post without document.location.href returns nothing.
What could I be doing wrong?
Big thanks to responders for their time!
Just like the answer in the post you linked says, you can't download a file via AJAX.
In order to set the location, change your action to respond to GET requests and either add your file id to the query string or setup a route to handle it. Also, and you may already be doing this, but you'll need to make sure you set the Content-Dispostion header value to attachment.
window.location.href = settings.action.downloadFile + "?fileId=" + fileIdVar
Since you are using jQuery, you could use $.param to build the parameters for you.
You could also look into a plugin to provide an "AJAX like" experience.
I'm using an .aspx page to serve an image file from the file system according to the given parameters.
Server.Transfer(imageFilePath);
When this code runs, the image is served, but no Last-Modified HTTP Header is created.
as opposed to that same file, being called directly from the URL on the same Server.
Therefor the browser doesn't issue an If-Modified-Since and doesn't cache the response.
Is there a way to make the server create the HTTP Headers like normally does with a direct request of a file (image in that case) or do I have to manually create the headers?
When you make a transfer to the file, the server will return the same headers as it does for an .aspx file, because it's basically executed by the .NET engine.
You basically have two options:
Make a redirect to the file instead, so that the browser makes the request for it.
Set the headers you want, and use Request.BinaryWrite (or smiiliar) to send the file data back in the response.
I'll expand on #Guffa's answer and share my chosen solution.
When calling the Server.Transfer method, the .NET engine treats it like an .aspx page, so It doesn't add the appropriate HTTP Headers needed (e.g. for caching) when serving a static file.
There are three options
Using Response.Redirect, so the browser makes the appropriate request
Setting the headers needed and using Request.BinaryWrite to serve the content
Setting the headers needed and calling Server.Transfer
I choose the third option, here is my code:
try
{
DateTime fileLastModified = File.GetLastWriteTimeUtc(MapPath(fileVirtualPath));
fileLastModified = new DateTime(fileLastModified.Year, fileLastModified.Month, fileLastModified.Day, fileLastModified.Hour, fileLastModified.Minute, fileLastModified.Second);
if (Request.Headers["If-Modified-Since"] != null)
{
DateTime modifiedSince = DateTime.Parse(Request.Headers["If-Modified-Since"]);
if (modifiedSince.ToUniversalTime() >= fileLastModified)
{
Response.StatusCode = 304;
Response.StatusDescription = "Not Modified";
return;
}
}
Response.AddHeader("Last-Modified", fileLastModified.ToString("R"));
}
catch
{
Response.StatusCode = 404;
Response.StatusDescription = "Not found";
return;
}
Server.Transfer(fileVirtualPath);
I would like to use jquery to send Table-data to the server and then get a data.csv file to download
if i navigate to the url in my browser like this:
http://localhost:49400/File/Csv/?Text=qweerty&Filename=asdf
i get promted with a file to download.
This is the action im calling:
public FileResult Csv(FileModel fileModel)
{
return File(Encoding.UTF8.GetBytes(fileModel.Text), "text/plain", string.Concat(fileModel.Filename, ".csv"));
}
and my javascript looks like this:
$("table").click(function () {
$.ajax({
type: "POST",
url: "http://localhost:49400/File/Csv/",
data: {"Text": "qwerty", "Filename": "asdf"}
})
});
The response in firebug is containing the data, but i would like it to ask the user if it wants to download it, is this possible?
You cannot use AJAX to download a file.
Instead, you can just set location to a URL pointing to the file.
As long as the server returns a downloadable file (with a Content-Disposition header), the browser will show a Save dialog and will not replace the page.
If you want to download the file from a POST, you could make a hidden <form> that sends the POST, then submit() the form using Javascript.
you can't deliver file content through ajax call, perhaps find a way to construct URL with your table data as query string and stream the file that way or post-back the whole page and response as a stream
I am using the code,
string loadFile = HttpContext.Current.Request.Url.AbsoluteUri;
// this.Response.ClearContent();
// this.Response.ClearHeaders();
this.Response.AppendHeader("content-disposition", "attachment; filename " + filename);
this.Response.ContentType ="application/html";
this.Response.WriteFile("C:\\Users\\Desktop\\Jobspoint Website\\jobpoint3.0\\print.aspx");
this.Response.Flush();
this.Response.Close();
this.Response.End();
to download an aspx page in asp.net C#.. But its only showing the html tags and static values... How can I save the entire page without html tags and with the values that retrieved from the database?
Thanks...
Leema
Use WebClient for this. It will download your file.
If I have understood correctly, one option would be to actually make a request to the web server using WebClient for example. And then write the response to that request to the Response.OutputStream. This means that the server will actually make a second request to it self and then send the response to the second request back to the client.
This way you will have the web server actually process the request and return the resulting HTML back to you rather than just the raw aspx page.
I have a Silverlight application hosted in an ASP.NET site, through which I'm starting an HttpWebRequest to a Generic Handler in order to save a CSV file to the user's machine.
From the Silverlight app, a Uri is constructed with parameters to make the CSV file server-side. A button is clicked which triggers the following:
string httpHandlerName = "HttpDownloadHandler.ashx";
// CustomUri handles making it an absolute Uri wherever we move the handler.
string uploadUrl = new CustomUri(httpHandlerName).ToString();
UriBuilder httpHandlerUrlBuilder = new UriBuilder(uploadUrl);
httpHandlerUrlBuilder.Query = string.Format("{3}startdate={0}&enddate={1}&partnerId={2}", startDate, endDate, partnerId, string.IsNullOrEmpty(httpHandlerUrlBuilder.Query) ? "" : httpHandlerUrlBuilder.Query.Remove(0, 1) + "&");
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(httpHandlerUrlBuilder.Uri);
webRequest.Method = "POST";
webRequest.BeginGetResponse(new AsyncCallback(GetResponseCallback), webRequest);
Now here is the ProcessRequest code from the HttpDownloadHandler.ashx
public void ProcessRequest(HttpContext context)
{
_httpContext = context;
string partnerId = _httpContext.Request.QueryString["partnerId"];
string startDate = _httpContext.Request.QueryString["startDate"];
string endDate = _httpContext.Request.QueryString["endDate"];
ExportCsvReport exportCsv = new ExportCsvReport();
_csvReport = exportCsv.ExportMemberRegistrationReport(partnerId, startDate, endDate);
context.Response.Clear();
context.Response.AddHeader("content-disposition", "attachment; filename=Report.csv");
context.Response.ContentType = "text/csv";
context.Response.Write(_csvReport);
}
Here is the HttpResponse header information that comes back when the Save File Dialogue refuses to appear:
{System.Web.HttpResponse}
Buffer: true
BufferOutput: true
Cache: {System.Web.HttpCachePolicy}
CacheControl: "private"
Charset: "utf-8"
ContentEncoding: {System.Text.UTF8Encoding}
ContentType: "text/csv"
Cookies: {System.Web.HttpCookieCollection}
Expires: 0
ExpiresAbsolute: {1/1/0001 12:00:00 AM}
Filter: {System.Web.HttpResponseStreamFilterSink}
HeaderEncoding: {System.Text.UTF8Encoding}
Headers: 'context.Response.Headers' threw an exception of type 'System.PlatformNotSupportedException'
IsClientConnected: true
IsRequestBeingRedirected: false
Output: {System.Web.HttpWriter}
OutputStream: {System.Web.HttpResponseStream}
RedirectLocation: null
Status: "200 OK"
StatusCode: 200
StatusDescription: "OK"
SubStatusCode: 'context.Response.SubStatusCode' threw an exception of type 'System.PlatformNotSupportedException'
SuppressContent: false
TrySkipIisCustomErrors: false
When I navigate to localhost/HttpDownloadHandler.ashx while the site is up, without initiating it from within the Silverlight app - the Save File Dialogue appears just fine, it seems to be a case where Silverlight is not accepting the response header properly.
Is there anything that can be done to address this? I'm open to suggestions for changing the way I'm doing this of course.
The response is going to Silverlight, not to the web browser (so the browser won't handle the CSV file and display a file save dialog). You need to initiate the request from the web browser directly (through JavaScript for example). You could use Silverlight's HTML/JavaScript bridge to do this pretty easily.
A reasonable example of the JavaScript bridge can be found here.
You need to add some logic like this:
HtmlPage.Window.Invoke("startDownload", httpHandlerUrlBuilder.Uri.ToString());
And then in the JavaScript:
<script type="text/javascript">
function startDownload(url){
// you'll probably need to redirect
// to a hidden iFrame to actually
// kick off the download, by
// setting the location to
// the url
// or ... some other option
// there are a number of
// different ways.
}
</script>
Also, you probably could do the same trick through the HTML DOM, from within Silverlight entirely. The link above has the basics regarding that as well.
As far as I know, Save Dialog will only be invoked in the Button click event, so when you receive http response, you will not get permission to open save dialogbox at all.
What you should do is, in your any button click event, probably download button, in the click event, you should call File Dialog and open the file stream that you will be using later on when you receive web server's response.