Почему это так сложно и как мне это решить?
На самом деле, это не так сложно:
public ActionResult GetPdf(string filename)
{
using (var client = new WebClient())
{
var buffer = client.DownloadData("http://foo.com/bar.pdf");
return File(buffer, "application/pdf", "report1.pdf");
}
}
Теперь очевидно, что у этого метода есть серьезный недостаток, поскольку он буферизует файл в памяти. Хотя это может хорошо работать для небольших отчетов, это может быть проблематично для больших файлов и еще более проблематично, если у вас много пользователей, которым не терпится приложить руку к этому великолепному отчету.
Есть также еще один серьезный недостаток первого действия контроллера. Это смешивает обязанности. Он содержит код инфраструктуры, и я призываю вас выполнить его модульное тестирование в отдельности.
Итак, давайте решим эти 2 серьезные проблемы, написав результат пользовательского действия:
public class ReportResult : ActionResult
{
private readonly string _filename;
public ReportResult(string filename)
{
_filename = filename;
}
public override void ExecuteResult(ControllerContext context)
{
var cd = new ContentDisposition
{
FileName = _filename,
Inline = false
};
var response = context.HttpContext.Response;
response.ContentType = "application/pdf";
response.Headers["Content-Disposition"] = cd.ToString();
using (var client = new WebClient())
using (var stream = client.OpenRead("http://foo.com/" + _filename))
{
// in .NET 4.0 implementation this will process in chunks
// of 4KB
stream.CopyTo(response.OutputStream);
}
}
}
, который вы будете использовать следующим образом:
public ActionResult GetPdf(string filename)
{
return new ReportResult(filename);
}
и по вашему мнению:
@Html.ActionLink("Download report", "GetPdf", new { filename = "report.pdf" })
Или вы можете полностью усомниться в полезности действия вашего контроллера, потому что, по вашему мнению, вместо:
@Html.ActionLink("Download report", "GetPdf")
вы можете иметь непосредственно:
<a href="http://foo.com/bar.pdf">Download report</a>
при условии, что клиент, конечно, имеет доступ к этому серверу.
Примечание: будьте очень осторожны с именами файлов, которые вы отправляете в заголовке Content-Disposition
. Я вижу в вашем вопросе, что вы использовали что-то вроде Server.UrlEncode("report1.pdf")
. Проверьте следующий вопрос на кошмар, в который это может превратиться.