为什么这么难,我该如何解决?
其实没那么难:
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");
}
}
现在显然这种方法存在一个严重的缺陷,因为它将文件缓冲在内存中。虽然这对于小型报告可能很有效,但对于大型文件可能会出现问题,如果您有很多用户迫不及待地想要处理这份出色的报告,问题会更加严重。
第一个控制器动作还有另一个严重缺陷。它混合了责任。它包含基础架构代码,我要求您单独对其进行单元测试。
所以让我们通过编写自定义操作结果来解决这两个严重问题:
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") 之类的东西。查看following question 了解这可能变成的噩梦。