2

I need to show a link to a file but only if the file exists. Thus far I have tried extending UrlHelper, HtmlHelper, and MvcHtmlString but none of them seem to give the results I need.

I'm sure I'm doing something wrong but I don't know what that is. The UrlHelper extension seems pretty close but the View renders the link as text instead of an anchor.

public static class UrlHelperExtensions
{
    public static string Pdf(this UrlHelper helper, string fileName, string directory)
    {
        string _fileName = fileName + ".pdf";
        string _directory = directory + "/";

        string root = "~/Content/Templates/";

        string path = HttpContext.Current.Server.MapPath(root + _directory + _fileName);

        if (File.Exists(path))
        {
            return helper.Content("<a href=\"" + root + _directory + _fileName + "\" target=\"_blank\">Download Template</a>");
        }
        else
        {
            return "";
        }            
    }
}

And then @Url.Pdf(Model.Item.Number, "Retail") gives me the text

<a href="~/Content/Templates/Retail/1001.pdf" target="_blank">Download Template</a>

on the page instead of an actual link.

2 Answers 2

4

You have to use HtmlString like so:

public static HtmlString Pdf(this UrlHelper helper, string fileName, string directory)
{
    string _fileName = fileName + ".pdf";
    string _directory = directory + "/";

    string root = "~/Content/Templates/";

    string path = HttpContext.Current.Server.MapPath(root + _directory + _fileName);

    if (File.Exists(path))
    {
        return new HtmlString(helper.Content("<a href=\"" + root.Replace("~", "") + _directory + _fileName + "\" target=\"_blank\">Download Template</a>"));
    }
    else
    {
        return new HtmlString("");
    }            
}

Edit: Fixed the root string.

Sign up to request clarification or add additional context in comments.

3 Comments

Alternatively you can generate the path only from the helper extension and then in the view go <a href='@Url.Pdf("fileName", "directory")' target="_blank">Download Template</a>. Although it might not work great if you dont want a link to be populated for non-existent files.
This is returning an incorrect path, i.e. it returns http://localhost:50467/Item/Details/~/Content/Templates/Retail/1001.pdf when it should be returning http://localhost:50467/Content/Templates/Retail/1001.pdf It is appending the path to the current location instead of to the root directory.
Remove the ~ from your root string for helper.Content() but not for MapPath().
1

Here goes one more solution -

Have your extension this way (I slightly modified the URL formations, instead of direct linking of file, I am making it to hit DownloadFile Action of FileController with Directory Name and File Name as parameters)

public static class UrlHelperExtensions
{
    public static MvcHtmlString Pdf(this UrlHelper helper, string fileName, string directory)
    {
        string _fileName = fileName + ".pdf";
        string _directory = directory + "/";

        string root = "~/Content/";

        string path = HttpContext.Current.Server.MapPath(root + _directory + _fileName);

        if (File.Exists(path))
        {
            return new MvcHtmlString("<a href=Downloadfile/" + _directory + fileName + " target=\"_blank\">Download Template</a>");
        }
        else
        {
            return new MvcHtmlString(string.Empty);
        }
    }
}

Then have a route in place -

routes.MapRoute(
    name: "DownloadfileRoute",
    url: "{controller}/{action}/{directoryname}/{filename}",
    defaults: new { controller = "FileController", action = "DownloadFle" }
);

Now have the action defined -

    public FileResult Downloadfile(string directoryname, string filename)
    {
        string _fileName = filename + ".pdf";
        string _directory = directoryname + "/";
        string root = "~/Content/";

        string path = Server.MapPath(root + _directory + _fileName);
        return new FileContentResult(System.IO.File.ReadAllBytes(path), _fileName);
    }

So now when you run the view, generated anchor tag will be like this -

http://localhost:5738/File/Downloadfile/img/untitled

And when you click it, your action will hit with following values. then finally file will be downloaded.

enter image description here

PS - Make sure you have proper validations in your code.

4 Comments

Is there an advantage to using this over Raidri's solution? This seems (to my beginner's eyes) like a lot more work for the same result.
@Jason, In production environments, we do not give direct access to files, instead we route them through some handlers like I have shown. One critical example would be for File leeching, if you open up files then any site can link your file and drive all their unnecessary traffic to your site and there by costing bandwidth for you.
Ah, I see! I will leave Raidri's answer selected since it gave me specifically what I asked for but I think I will ultimately use your solution. Thank you very much!
@Jason, No problem. I am glad my answer helped you in learning.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.