【问题标题】:How to initialize file download from an external server?如何初始化从外部服务器下载文件?
【发布时间】:2012-02-15 10:17:44
【问题描述】:

我有一个这样定义的 MVC 控制器方法:

public ActionResult GetPdf(string filename)
        {
            var pdfDownload = File("~/Content/GeneratedReports/report1.pdf", "application/pdf", Server.UrlEncode("report1.pdf"));

            return pdfDownload;
        }

如果我将第一个参数更改为托管在单独云服务器上的服务器的 url,则会收到错误消息:

“我的文件路径”不是有效的虚拟路径。

我只是希望我的客户能够下载文件。这似乎比需要的复杂得多。

我有一个指向 PDF 的 URL。我希望我的客户无需单击任何内容即可下载该 pdf。 (服务响应成功后将开始下载)

为什么这么难,我该如何解决?

我不在乎解决方案是在 JS 还是 MVC 中......

【问题讨论】:

  • 使用javascript,你可以简单地做一个重定向:location.replace(externalUrlToDownload);

标签: c# javascript asp.net-mvc-3


【解决方案1】:

我正在做一个类似的例程。现在我让它从本地驱动器访问图像

byte[] cimage = new WebClient().DownLoadData(System.Web.HttpContent.Server.MapPath("~/Content/koala.jpg"));

我将如何访问我们的 SQL Server 上的共享。我在 SQL Server 上的文件夹中有要检索的图像。

byte[] cimage = new WebClient().DownLoadData("服务器共享" + /Files/koala.jpg");

【讨论】:

  • 编写一个公开这些资源的 Web 服务并从该服务下载文件。
【解决方案2】:

为什么这么难,我该如何解决?

其实没那么难:

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 了解这可能变成的噩梦。

【讨论】:

  • 好的,ActionResult 实现绝对值得投票(看一下数字),即使它会加速我自己的失败;p 个人我会使用参数很少,但它很好地显示了机制(编辑)该死,现在你也添加了参数;p
【解决方案3】:

您可以在远程报告中重定向用户;如果这不是一个选项,您将需要代理它:

byte[] blob;
using(var client = new WebClient()) {
    blob = client.DownloadData(remoteUrl);
}
return File(blob, "application/pdf", "report1.pdf");

以上假设文件不是很大;更健壮的实现会以块的形式获取和发送。

【讨论】:

    猜你喜欢
    • 2010-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多