7

I have a MVC-application. And I want to download a pdf.

This is a part of my view:

<p>
    <span class="label">Information:</span>
    @using (Html.BeginForm("DownloadFile")) { <input type="submit" value="Download"/> }
</p>

And this is a part of my controller:

private string FDir_AppData = "~/App_Data/";

public ActionResult DownloadFile()
{
    var sDocument = Server.MapPath(FDir_AppData + "MyFile.pdf");

    if (!sDocument.StartsWith(FDir_AppData))
    {
        // Ensure that we are serving file only inside the App_Data folder
        // and block requests outside like "../web.config"
        throw new HttpException(403, "Forbidden");
    }

    if (!System.IO.File.Exists(sDocument))
    {
        return HttpNotFound();
    }

    return File(sDocument, "application/pdf", Server.UrlEncode(sDocument));
}

How can I download the specific file?

user1531040
  • 2,143
  • 6
  • 28
  • 48

2 Answers2

9

Change the DownloadFile Action signature from:

 public ActionResult DownloadFile()

To:

 public FileResult DownloadFile()

In addition, I think that the UrlEncode of the file path is redundant, change it to:

return File(sDocument, "application/pdf", sDocument);

And make sure that this path does physically exist.

Yair Nevet
  • 12,725
  • 14
  • 66
  • 108
  • The file still are not downloaded. – user1531040 Sep 01 '14 at 23:24
  • I don't know. I expect the file under the download arrow (firefox). The site shows no errors. It looks like the system read something and than he finished. – user1531040 Sep 02 '14 at 06:38
  • 2
    This solution works for me (in MVC 4 and using Html.ActionLink in the razor view to build the download URL), so I much prefer it over the accepted answer: It not only looks like the simplest and cleanest solution, but also like what I imagine the MVC/Razor designers had in mind when they designed the API. – Kai Weber May 04 '17 at 07:41
9

Possible solution - provide form method and controller name:

@using (Html.BeginForm("DownloadFile", "Controller", FormMethod.Get))
        { <input type="submit" value="Download" /> }

or try to use action link instead of form:

@Html.ActionLink("Download", "DownloadFile", "Controller")

or try to provide direct url to the file:

<a href="~/App_Data/MyFile.pdf">Download</>

This isn't the best practice because of security reasons, but still you can try... Also, You can wrap file location to some @Html helper method:

public static class HtmlExtensions {
    private const string FDir_AppData = "~/App_Data/";

    public static MvcHtmlString File(this HtmlHelper helper, string name){
        return MvcHtmlString.Create(Path.Combine(FDir_AppData, name));
    }
}

And in the view:

<a href="@Html.File("MyFile.pdf")">Download</>
Dmytro
  • 16,668
  • 27
  • 80
  • 130
  • My problem was that the APP_DATA folder was a reserved name. I could not take files from that folder. So I had to move my file to another folder. – user1531040 Sep 07 '14 at 12:50
  • I would definitely use the ActionLink method because this allows you to use an ActionFilter to authorize/authenticate whether or not the user is able to download the file. Typically, the link shouldn't even be shown if a user doesn't have rights to download it, but if the url was somehow obtained by a 3rd party, you're still in control of the security. – jason Feb 21 '15 at 17:02