如何在 .NET MVC3 中截获当前 actionresult 的输出流?

Posted

技术标签:

【中文标题】如何在 .NET MVC3 中截获当前 actionresult 的输出流?【英文标题】:How do I intercept the output stream of the current actionresult in .NET MVC3? 【发布时间】:2012-08-04 23:31:22 【问题描述】:

您好,感谢您的关注!

背景

我正在使用Rotativa pdf 工具将视图 (html) 读入 PDF。它工作得很好,但它本身并不提供将 PDF 保存到文件系统的方法。相反,它只是将文件作为操作的结果返回给用户的浏览器。

代码如下所示:

public ActionResult PrintQuote(FormCollection fc)
        
            int revisionId = Int32.Parse(Request.QueryString["RevisionId"]);

            var pdf = new ActionAsPdf(
                 "Quote",
                 new  revisionId = revisionId )
                       
                           FileName = "Quote--" + revisionId.ToString() + ".pdf",
                           PageSize = Rotativa.Options.Size.Letter
                       ;

            return pdf;

         

此代码正在调用另一个操作结果(“引用”),将其视图转换为 PDF,然后将 PDF 作为文件下载返回给用户。

问题

如何截取文件流并将 PDF 保存到我的文件系统。将PDF发送给用户是完美的,但我的客户也希望将PDF同时保存到文件系统中。

有什么想法吗?

谢谢!

马特

【问题讨论】:

【参考方案1】:

看一下这里的 MVC 流水线图: http://www.simple-talk.com/content/file.ashx?file=6068

方法OnResultExecuted()在ActionResult被渲染后被调用。

您可以覆盖此方法或使用ActionFilter 来应用和使用属性的 OnResultExecuted 拦截器。

编辑: 在this forum thread 的末尾,您会找到一个回复​​,其中给出了一个 ActionFilter 的示例,它读取(并更改)动作的响应流。然后,您可以将流复制到文件中,并将其返回给您的客户端。

【讨论】:

问题是库直接写入OutputStream,我不认为你可以捕捉到? 我不确定,我希望 OutputStream 不会直接发送到客户端,而是提供给 ActionFilters。我编辑了答案以添加一个示例代码,该代码使用上述 ActionFilter 将响应作为流拦截。您可以轻松检查 Stream 是否包含您期望的数据。 在下面的 SO 问题中,答案提供了一个更简单的例子:***.com/questions/5424346/… 谢谢罗曼。我将不得不弄清楚如何实现这段代码,但你给了我一个好的开始。再次感谢。 再次感谢 Roman,但我遇到了一些麻烦。您认为“OnResultExecuted”覆盖应该去哪里?我把它放在我的控制器中,但它没有被调用。谢谢!【参考方案2】:

我有同样的问题,这是我的解决方案:

您基本上需要向自己的 URL 发出 HTTP 请求并将输出保存为二进制文件。简单、没有重载、辅助类和臃肿的代码。

你需要这个方法:

    // Returns the results of fetching the requested HTML page.
    public static void SaveHttpResponseAsFile(string RequestUrl, string FilePath)
    
        try
        
            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(RequestUrl);
            httpRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)";
            httpRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
            HttpWebResponse response = null;
            try
            
                response = (HttpWebResponse)httpRequest.GetResponse();
            
            catch (System.Net.WebException ex)
            
                if (ex.Status == WebExceptionStatus.ProtocolError)
                    response = (HttpWebResponse)ex.Response;
            

            using (Stream responseStream = response.GetResponseStream())
            
                Stream FinalStream = responseStream;
                if (response.ContentEncoding.ToLower().Contains("gzip"))
                    FinalStream = new GZipStream(FinalStream, CompressionMode.Decompress);
                else if (response.ContentEncoding.ToLower().Contains("deflate"))
                    FinalStream = new DeflateStream(FinalStream, CompressionMode.Decompress);

                using (var fileStream = System.IO.File.Create(FilePath))
                
                    FinalStream.CopyTo(fileStream);
                

                response.Close();
                FinalStream.Close();
            
        
        catch
         
    

然后在你的控制器中,你可以这样称呼它:

SaveHttpResponseAsFile("http://localhost:52515/Management/ViewPDFInvoice/" + ID.ToString(), "C:\\temp\\test.pdf");

然后瞧!该文件在您的文件系统中,您可以双击并打开 PDF,或通过电子邮件将其发送给您的用户,或者您需要的任何内容。

【讨论】:

文件保存后损坏,你知道这是为什么吗?【参考方案3】:

我成功使用了 Aaron 的“SaveHttpResponseAsFile”方法,但我不得不更改它,因为当前登录用户的凭据没有被应用(因此它被转发到 MVC4 的登录 url)。

public static void SaveHttpResponseAsFile(System.Web.HttpRequestBase requestBase, string requestUrl, string saveFilePath)

    try
    
        *snip*
        httpRequest.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
        httpRequest.Headers.Add(HttpRequestHeader.Cookie, requestBase.Headers["Cookie"]);
        *snip*</pre></code>

然后在调用 Controller 方法中,只需将“Request”添加到 SaveHttpResponseAsFile 调用中。

【讨论】:

【参考方案4】:

您也可以使用 Rotativa 来做到这一点,这实际上非常简单。

Using Rotativa;

...

byte[] pdfByteArray = Rotativa.WkhtmltopdfDriver.ConvertHtml( "Rotativa", "-q", stringHtmlResult );

File.WriteAllBytes( outputPath, pdfByteArray );

我在一个 winforms 应用程序中使用它,从我们也在 Web 应用程序中使用的 Razor Views 生成和保存 PDF。

【讨论】:

请注意,如果您显示 PDF 然后执行此操作,您将呈现两次。 为什么投反对票?如果你不告诉我原因,我就无法好转。我知道代码有效,现在正在生产中。 Brown 谢谢先生的回答,我想知道 ConvertHtml 采用的参数是什么,因为我无法弄清楚,或者你只是将我链接到它的文档 我将名称 (Rotativa.WkhtmltopdfDriver.ConvertHtml) 粘贴到 Google 中,这是第一个结果:(也许您应该尝试 Google 搜索?)github.com/webgio/Rotativa/blob/master/Rotativa/… 仍然无法获得关于它的直觉。第三个参数指的是什么?【参考方案5】:

我能够让 Eric Brown - Cal 的解决方案发挥作用,但我需要进行一些小调整以防止出现关于 找不到目录的错误>。

(另外,查看 Rotativa 代码,似乎 -q 开关已默认传递,因此可能没有必要,但我没有更改它。)

var bytes = Rotativa.WkhtmltopdfDriver.ConvertHtml(Server.MapPath(@"/Rotativa"), "-q", html);

【讨论】:

【参考方案6】:
 return new Rotativa.ActionAsPdf("ConvertIntoPdf") 
  
     FileName = "Test.pdf", PageSize = Rotativa.Options.Size.Letter
 ;

【讨论】:

以上是关于如何在 .NET MVC3 中截获当前 actionresult 的输出流?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.net MVC3 中获取 Advantage Database 数据?

如何在 ASP.NET MVC3 中使用“FormCollection”接收关联数组数据

asp.net mvc3 linq 多表查询怎么把返回的集合在页面循环输出

如何在 ASP.NET MVC3 中通过 HTTPS 提供静态文件(在 ~/Content 中)

如何使用 ASP.NET MVC3 编辑嵌套模型

如何在 asp.net mvc3 中使用 Kendo UI Editor 和剃须刀?