ASP.NET MVC 3: How to look for the controller instead of file when id is file name - asp.net-mvc-3

I've encountered with some strange problem. It's strange for me because I don't understand it and because before everything worked fine. So, my task is to call for controller and pass it file name (with extension) and controller should recognize this file, write into log and then return the file itself if it exists (in Downloads folder). What I'm doing:
public class DownloadController : Controller
{
public ActionResult Files(string id)
{
string filePath = Server.MapPath(Url.Content(string.Format("~/Downloads/{0}", id)));
string serverPath = Url.Content(string.Format("~/Downloads/{0}", id));
string ext = Path.GetExtension(filePath);
if (!System.IO.File.Exists(filePath))
{
//return to the error controller
}
string mem = "text/html";
if (ext == ".zip")
{
mem = "application/x-zip-compressed";
}
else if (ext == ".html" || ext == ".htm")
{
mem = "text/html";
}
else if (ext == ".pdf")
{
mem = "application/pdf";
}
//Save info about downloads into DB
repStat.SaveStatInfo(id, HttpContext.Request.UserHostAddress,
HttpContext.Request.UserHostName, HttpContext.Request.UserAgent);
return File(serverPath, mem, id);
}
}
There is part of the Global.asax:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("Content/{*pathInfo}");
It doesn't have more "ignores".
So, the problem is when I'm doing the call: mysite.com/download/files/test.pdf the server returns me "Page not found". Of course there is no such file in download/files path! It should call for the controller but not the real file. As soon as I delete the extension like mysite.com/download/files/test the server calls for the controller. I don't understand why it doesn't recognize the file name just as parameter and tries to find the file.
There is absolutely the same behaviour if I try to do with other controllers - as soon as parameter doesn't have any extension it works good else the server looks for the file.
The most strange thing is everything works good locally but doesn't work on the server (worked not tool long but then stopped).

It looks strange to me too. A workaround would be to encode the dot (.) like, for example,
test.pdf -> test++pdf
and then decode it then decode it back.
Probably not the best solution, but will certainly solve the problem.

Related

not a valid virtual path - when trying to return a file from a url

We download a file from our CdN and then return a url to that downloaded file to the user. I'm trying to get this implemented so that when a user clicks the download buttton, it goes and test that url to that downloaded file then forces a save prompt based on that local url.
So for example if there is a button called download on the page for a specific .pdf, we ultimately have code in our controller going to the cdn and downloading the file, zipping it then returning a url such as: http://www.ourLocalAssetServer.com/assets/20120331002728.zip
I'm not sure if you you can use the File() method to return the resource to the user as to cause a save prompt when you have a url to the file, not a system directory virtual path.
So how can I get this working with the url? I need the download button to ultimately force a save prompt on their end given a url such as what is generated per this example above? Not I am using POST, not a GET, so not sure which I should use in this case either besides this not working overall to force a save prompt. It is hitting my GetFileDownloadUrl but ultimately errors saying it's not a virtual path.
Here's my code:
#foreach (CarFileContent fileContent in ModelCarFiles)
{
using (Html.BeginForm("GetFileDownloadUrl", "Car", FormMethod.Get, new { carId = Model.CarId, userId = Model.UserId, #fileCdnUrl = fileContent.CdnUrl }))
{
#Html.Hidden("userId", Model.UserId);
#Html.Hidden("carId", Model.CarId);
#Html.Hidden("fileCdnUrl", fileContent.CdnUrl);
<p><input type="submit" name="SubmitCommand" value="download" /> #fileContent.Name</p>
}
}
public ActionResult GetFileDownloadUrl(string fileCdnUrl, int carId, int userId)
{
string downloadUrl = string.Empty;
// take the passed Cdn Url and go and download that file to one of our other servers so the user can download that .zip file
downloadUrl = GetFileZipDownloadUrl(carId, userId, fileCdnUrl;
// now we have that url to the downloaded zip file e.g. http://www.ourLocalAssetServer.com/assets/20120331002728.zip
int i = downloadUrl.LastIndexOf("/");
string fileName = downloadUrl.Substring(i);
return File(downloadUrl, "application/zip", fileName);
}
error: not a valid virtual path
This won't work except the zip file is in your virtual path.
The File method you have used here File(string, string, string) expects a fileName which will be used to create a FilePathResult.
Another option would be to download it (using WebClient.DownloadData or DownloadFile methods) and passing either the byte array or the file path (depending on which you choose).
var webClient = new Webclient();
byte[] fileData = webClient.DownloadData(downloadUrl);
return File(fileData, "application/zip", fileName);
And the lines where you get the index of "/" just to get the filename is unnecessary as you could have used:
string fileName = System.IO.Path.GetFileName(downloadUrl);

Clicking Open Word Document tries to reconnect to Controller action while downloading

I have a requirement to download a file from the server, but open inline if possible. I'm currently doing;
Response.AddHeader("Content-Disposition", string.Format("inline; filename={0}", documentFileName));
result = new FileStreamResult(new FileStream(documentFilePath, FileMode.Open), "application/msword");
I've put application/msword in there right now, because that's what I'm having a problem with. When I click Open on the word document, it's as if the document makes multiple calls back to the action, but there is no session and no database so it crashes. When the user is running this, they see a long hang, the "Downloading" dialog finally appears in word and they have to cancel it. The document is there and is valid but this is not desirable.
Pdfs, pngs etc. download fine. Can anybody explain this behavior, and give me some hints as to how I fix it?
Update:
The action basically looks like;
[HttpPost]
public FileResult View(int id, int source)
{
var document = GetDocumentFromDatabase(id, source);
documentFilePath = Path.Combine( documentsDirectory, document.Name);
documentName = document.Name;
Response.AddHeader("Content-Disposition", string.Format("inline; filename={0}", documentFileName));
result = new FileStreamResult(new FileStream(documentFilePath, FileMode.Open), "application/msword");
return result;
}
I've trimmed it down, as I can't share the specifics, but the full idea is there.
Answer:
I have a lookup of available content-types, in there I have defined whether the file is inline or attachment, and when I detect a word document, I set it to attachment. No more error. PDF opens in the browser still because I set it to inline.
I use:
public ActionResult GetAttachment(int id)
{
var attachment = _repository.GetAttachByID(id);
if (attachment != null)
{
Response.AppendHeader("Content-Disposition",string.Format("inline; filename={0}",attachment.FileName));
return File(attachment.File, attachment.MimeType, attachment.FileName);
}
else
{
return null;
}
}
Regards

MVC 3 VirtualPathUtility Issue

I have an MVC 3 app with a Configuration folder that holds XML files specific to my application. I'm trying to enumerate the files in a view like this:
string configPath = VirtualPathUtility.ToAbsolute("~/Configuration");
foreach (string path in Directory.EnumerateFiles(configPath, "*.xml"))
{
// ...
}
However, the path resolves to C:\Configuration as evidenced by the error message:
Could not find a part of the path 'C:\Configuration\'.
What am I missing?
I don't know but this should work in your controller:
string configPath = Server.MapPath("~/Configuration");

Returning an MVC FileContentResult to download .pdf file from

Hi I've search around for this quite a bit but I didn't find a situation that really resembled mine..hope I didn't miss a duplicate somewhere
The Goal: Return a file from a UNC share to the client as a download/open option.
Info: The share is located on a different server than the one hosting the web site. When a corresponding folder name on the menu is clicked, I am able to successfully read from the share (I return the files as a JSON result) and in Jquery I then append list items for each file found in the folder and make the list item ID's the filename. This works great.
When these appended list items are clicked on I pass their ID's (which are the filename, like "thefile.pdf") to the following controller which returns a FileContentResult.
files[0].ToString() below is similar to "\server\folder\"
public ActionResult OpenTheFile(string id)
{
List<string> files = new List<string>();
files.AddRange(Directory.GetFiles(LNFiles.ThePath, id, SearchOption.AllDirectories));
Response.AppendHeader("Content-Disposition", "attachment; filename=" + id + ";");
return File(files[0].ToString(), System.Net.Mime.MediaTypeNames.Application.Pdf, id);
}
And yes the obligatory "it works on my local machine". When deployed to the IIS 7.5 server and I click on the list item I get this YSOD error:
The handle is invalid. (Exception from
HRESULT: 0x80070006 (E_HANDLE))
I'm impersonating a user with rights to the file share...I'm at a loss, i was thinking something with encoding or screwed up rights? I've tried using a virtual dir instead but alas same issue.
In my case
changing this:
public ActionResult Download(int id)
{
var item = ItemRepo.GetItemById(id);
string path = Path.Combine(Server.MapPath("~/App_Data/Items"), item.Path);
return File(path, "application/octetstream", item.Path);
}
to this:
public ActionResult Download(int id)
{
var item = ItemRepo.GetItemById(id);
string path = Path.Combine(Server.MapPath("~/App_Data/Items"), item.Path);
return File(new FileStream(path, FileMode.Open), "application/octetstream", item.Path);
}
has worked. I am putting this here just in case anyone needs.
Check out this for a workaround
You may want to try a packet capture to see if you are receiving the same issue as documented here:
http://forums.asp.net/t/1473379.aspx/1
For your unc path - are you directly referencing \servername\share or are you using a network mapped drive letter?
God Bless you : ProgRockCode.
and since that involved an ActionResult, I wrote a custom ActionResult that used the "WriteFile" method.
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.WriteFile(FilePath, true);
context.HttpContext.Response.End();
}

MVC 3 ContentResult binary file with firefox strange issue

I've got an MVC 3 app that allows users to upload files with some data entry stuff. I've set up a controller that fetches those documents and buffers them out to the user like so
[OutputCache(Duration = 1200, VaryByParam = "id")]
public ContentResult GetNarrative(int id)
{
Response.Clear();
Response.BufferOutput = true;
Response.ContentType = "application/octet-stream";
var narrative = attachRepo.GetNarrative(id);
if (narrative == null || narrative.Narrative == null)
return null;
Response.AddHeader("Content-Disposition",
string.Format("attachment;filename={0}",
Server.UrlEncode(narrative.Filename)));
Response.OutputStream.Write(narrative.Narrative.ToArray(),
0, narrative.Narrative.ToArray().Length);
Response.OutputStream.Flush();
return Content("");
}
This works fine and well, the interesting thing is that when I have the output cache line, my firefox download dialog looks like this
However when I comment out the output cache line it looks like the expected dialog
This isn't really a blocking issue, as it works just fine in IE and Chrome just downloads by default, but I am curious why this would be happening and if anyone has experienced this and worked around it.
Thanks!
I've found that firefox ignores the filename in the Attachment. It tends to use he URL to set the filename.
Try putting at the end of the URL ?filename.docx
And see if it fixes things.

Resources