如何从外部服务器初始化文件下载? [英] How to initialize file download from an external server?

查看:160
本文介绍了如何从外部服务器初始化文件下载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 公众的ActionResult GetPdf(字符串文件名)
        {
            VAR pdf档案下载=文件(〜/内容/ GeneratedReports / report1.pdf,应用程序/ PDF,Server.UrlEn code(report1.pdf));            返回pdf档案下载;
        }

如果我改变的第一个参数举办单独的云服务器上的服务器的URL,然后我得到的错误:


  

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


我只希望我的客户能够下载文件。这看起来很复杂得多比它需要的话。

我有一个指向PDF的URL。我希望我的客户端下载的PDF没有任何点击。 (下载将开始在成功服务响应)

这是为什么这么难?如何解决呢?

我不介意的解决方案是JS或MVC ....


解决方案

  

这是为什么这么难?如何解决呢?


其实,这并不难:

 公众的ActionResult GetPdf(字符串文件名)
{
    使用(VAR的客户=新的WebClient())
    {
        VAR缓冲= client.DownloadData(http://foo.com/bar.pdf);
        返回文件(缓冲区,应用程序/ PDF,report1.pdf);
    }
}


现在显然有使用这种方法,因为它是buferring内存中的文件的一个严重缺陷。虽然这可以为小型工作报告很大,如果你有很多用户不耐烦地把自己的手这个大报告中也可能是有问题的大文件,甚至更多的问题。

还有与第一控制器动作的另一个严重的缺陷。它混合责任。它包含了基础设施code和我挑战你单位单独进行测试。

因此​​,让我们解决这些2严重的问题,通过编写自定义作用的结果:

 公共类ReportResult:的ActionResult
{
    私人只读字符串_filename;
    公共ReportResult(字符串的文件名)
    {
        _filename =文件名;
    }    公共覆盖无效的ExecuteReuslt(ControllerContext上下文)
    {
        VAR CD =新ContentDisposition
        {
            文件名= _filename,
            内嵌= FALSE
        };
        VAR响应= context.HttpContext.Response;
        response.ContentType =应用程序/ PDF
        response.Headers [内容处置] = cd.ToString();        使用(VAR的客户=新的WebClient())
        使用(VAR流= client.OpenRead(http://foo.com/+ _filename))
        {
            //在.NET 4.0中实现,这将在块处理
            4KB的//
            stream.CopyTo(response.OutputStream);
        }
    }
}

这是你会使用这样的:

 公众的ActionResult GetPdf(字符串文件名)
{
    返回新ReportResult(文件名);
}

和在您的视图:

  @ Html.ActionLink(下载报告,GetPdf,新的{filename =report.pdf})


或者你可以完全,因为在你看来,而不是质疑你的控制器动作的用处:

  @ Html.ActionLink(下载报告,GetPdf)

您可以直接有:

 < A HREF =htt​​p://foo.com/bar.pdf>下载报告< / A>

假设客户端访问,当然这台服务器。

注:必须非常小心,你要发送的内容处置头文件名。我看到你的问题,你使用像 Server.UrlEn code(report1.pdf)。结帐的<一个href=\"http://stackoverflow.com/questions/93551/how-to-en$c$c-the-filename-parameter-of-content-disposition-header-in-http\">following问题了解,这可能变成噩梦。

I have an MVC controller method defined like this:

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

            return pdfDownload;
        }

If i change the first parameter to the url of a server hosted on a separate cloud server then I get the error :

'MY FILE PATH' is not a valid virtual path.

I just want my client to be able to download a file. This seems so much more complex than it need be.

I have a URL that points to a PDF. I want my client to download that pdf without clicking anything. (The download will initiate upon successful service response)

Why is this so hard and how do i solve it ?

I don't care if the solution is in JS or MVC....

解决方案

Why is this so hard and how do i solve it ?

Actually, it's not that hard:

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");
    }
}


Now obviously there's a serious flaw with this method as it is buferring the file in memory. While this could work great for small reports, it could be problematic with large files and even more problematic if you have lots of users impatient to put their hands on this great report.

There's also another serious flaw with the first controller action. It mixes responsibilities. It contains infrastructure code and I challenge you to unit test it in isolation.

So let's solve those 2 serious problems by writing a custom action result:

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);
        }
    }
}

that you will use like this:

public ActionResult GetPdf(string filename)
{
    return new ReportResult(filename);
}

and in your view:

@Html.ActionLink("Download report", "GetPdf", new { filename = "report.pdf" })


Or you could totally question the usefulness of your controller action because in your view instead of:

@Html.ActionLink("Download report", "GetPdf")

you could have directly:

<a href="http://foo.com/bar.pdf">Download report</a>

assuming the client has access to this server of course.

Remark: be very careful with the filenames you are sending in the Content-Disposition header. I see in your question that you used something like Server.UrlEncode("report1.pdf"). Checkout the following question for the nightmare that this could turn into.

这篇关于如何从外部服务器初始化文件下载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆