如何在 .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 多表查询怎么把返回的集合在页面循环输出