Reputation: 175
I generate reports on my MVC application witch I then export to pdf and witch is then saved in my ~/Reports/Invoices/
Directory. I want to download these files one by one.
Here is my class:
public class DownloadResult : ActionResult
{
public DownloadResult()
{
}
public DownloadResult(string virtualPath)
{
this.VirtualPath = virtualPath;
}
public string VirtualPath
{
get;
set;
}
public string FileDownloadName
{
get;
set;
}
public override void ExecuteResult(ControllerContext context) {
if (!String.IsNullOrEmpty(FileDownloadName)) {
context.HttpContext.Response.AddHeader("content-disposition",
"attachment; filename=" + this.FileDownloadName);
}
string filePath = context.HttpContext.Server.MapPath(this.VirtualPath);
context.HttpContext.Response.TransmitFile(filePath);
}
}
and here is my controller action:
public ActionResult Download(int id)
{
return new DownloadResult
{
VirtualPath = "~/Reports/Invoices/" + Table.Where(x => x.ID == id).FirstOrDefault().ID + ".pdf",
FileDownloadName = Table.Where(x => x.ID == id).FirstOrDefault().ID.ToString()
};
}
When I try to use this code all it does is fill the partial view with symbols like when you try to open a binary file in notepad.
Any ideas on what I'm doing wrong?
I have Tried this:
public FileResult Download(int id)
{
byte[] fileBytes = System.IO.File.ReadAllBytes(Server.MapPath("~/Reports/Invoices/" + Table.Where(x => x.ID == id).FirstOrDefault().ID + ".pdf"));
string fileName = Table.Where(x => x.ID == id).FirstOrDefault().ID.ToString();
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Pdf, fileName);
}
And get the same results
Invoices.GridLoadDone = function () {
$.contextMenu({
selector: '#gbox_invoiceGrid',
callback: function (key, options) {
var m = "clicked: " + key;
switch (key) {
case "view":
Globals.PerformAjaxFromHyperlink(null, '#/Invoice/View/' + Invoices.CurrentRow, true, Invoices.CurrentRow);
Globals.SetUrl('#/Invoice/View/' + Invoices.CurrentRow, false);
return true;
break;
case "email":
Globals.PerformAjaxFromHyperlink(null, '/Invoice/Email/' + Invoices.CurrentRow, false);
break;
case "download":
Globals.PerformAjaxFromHyperlink(null, '/Invoice/Download/' + Invoices.CurrentRow, false);
//$("#readingsGrid").jqGrid('editRow', Readings.CurrentRow,
break;
}
},
items: {
"view": {
name: "View Invoice"
},
"email": {
name: "Email Invoice"
},
"download": {
name: "Download Invoice"
},
}
});
Upvotes: 0
Views: 1566
Reputation: 175
Below you wil find my latest attempt. Even if I specify the content type
to application/pdf
or application/octet-stream
it still returns it in the view as text.
public static void TransmitFile(string url)
{
string ext = url.Substring(url.LastIndexOf("."), url.Length - url.LastIndexOf("."));
string filename = url.Substring(url.LastIndexOf("/") + 1, url.Length - ext.Length - url.LastIndexOf("/") - 1).Replace(" ", "_");
if (filename + ext == url)
{
filename = filename.Replace(AppDomain.CurrentDomain.BaseDirectory, "").Replace("\\", "/");
filename = filename.Substring(filename.LastIndexOf("/") + 1, filename.Length - filename.LastIndexOf("/") - 1);
}
if (url.Contains("\\"))
url = url.Replace(AppDomain.CurrentDomain.BaseDirectory, "").Replace("\\", "/");
RegistryKey key = Registry.ClassesRoot.OpenSubKey(ext);
string contentType = key.GetValue("Content Type").ToString();
try
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = contentType;
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename + ext);
HttpContext.Current.Response.TransmitFile(HttpContext.Current.Server.MapPath(url));
HttpContext.Current.Response.End();
}
catch { }
}
}
Upvotes: 0
Reputation: 11396
IMHO, you probably want to stay away from writing physical files within your site structure.
This not only prevents you from being able to deploy the application to multiple servers on a webfarm, but it also might complicate deployment using tools such as WebDeploy.
You might want to think about storing them either on a database or on some other shared location outside of the IIS web application.
Also, for file downloads there's no need to use ajax, a standard GET will do, such as just using a regular hyperlink. However, I wouldn't recommend passing virtual paths around as parameters from client to the server.
Instead, how about having in your DownloadController
an action called Report
, which receives only the id as parameter?
You could end up with more friendly URLs, such as: http://somewhere.com/download/report/3 or http://somewhere.com/download/report?id=3
Within that action, you can just use the method mentioned in the comments, using return File()
and FileResult
. With these you can either return the byte[]
contents or a virtual path directly, such as: File("~/virtualpath", [content-type])
, and also specify a name for the downloaded file as well.
The reason you might be seeing the binary content is because you aren't issuing a standard GET and letting the browser handle the response but using ajax instead.
Upvotes: 1