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

时间:2022-01-06 00:29:00

Hi and thanks for looking!

嗨,谢谢你的期待!

Background

I am using the Rotativa pdf tool to read a view (html) into a PDF. It works great, but it does not natively offer a way to save the PDF to a file system. Rather, it only returns the file to the user's browser as a result of the action.

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

Here is what that code looks like:

这是代码的样子:

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;

        } 

This code is calling up another actionresult ("Quote"), converting it's view to a PDF, and then returning the PDF as a file download to the user.

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

Question

How do I intercept the file stream and save the PDF to my file system. It is perfect that the PDF is sent to the user, but my client also wants the PDF saved to the file system simultaneously.

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

Any ideas?

Thanks!

Matt

6 个解决方案

#1


3  

I have the same problem, here's my solution:

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

You need to basically make an HTTP request to your own URL and save the output as a binary file. Simple, no overload, helper classes, and bloated code.

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

You'll need this method:

你需要这个方法:

    // 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
        { }
    }

Then inside your controller, you call it like this:

然后在你的控制器内,你这样称呼它:

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

And voilà! The file is there on your file system and you can double click and open the PDF, or email it to your users, or whatever you need.

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

#2


1  

 return new Rotativa.ActionAsPdf("ConvertIntoPdf") 
 { 
     FileName = "Test.pdf", PageSize = Rotativa.Options.Size.Letter
 };

#3


0  

Take a look at the MVC pipeline diagram here: http://www.simple-talk.com/content/file.ashx?file=6068

在这里查看MVC管道图:http://www.simple-talk.com/content/file.ashx?file = 6068

The method OnResultExecuted() is called after the ActionResult is rendered.

在呈现ActionResult之后调用OnResultExecuted()方法。

You can override this method or use an ActionFilter to apply and OnResultExecuted interceptor using an attribute.

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

Edit: At the end of this forum thread you will find a reply which gives an example of an ActionFilter which reads (and changes) the response stream of an action. You can then copy the stream to a file, in addition to returning it to your client.

编辑:在此论坛主题的末尾,您将找到一个回复​​,其中提供了一个ActionFilter的示例,该ActionFilter读取(并更改)操作的响应流。然后,您可以将流复制到文件,还可以将其返回到客户端。

#4


0  

I successfully used Aaron's 'SaveHttpResponseAsFile' method, but I had to alter it, as the currently logged in user's credentials weren't applied (and so it was forwarding to MVC4's login url).

我成功使用了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>

Then in your calling Controller method, simply add 'Request' into the SaveHttpResponseAsFile call.

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

#5


0  

You can also do it using Rotativa, which is actually quite easy.

您也可以使用Rotativa来实现,这实际上非常简单。

Using Rotativa;

...

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

File.WriteAllBytes( outputPath, pdfByteArray );

I'm using this in a winforms app, to generate and save the PDFs from Razor Views we also use in our web apps.

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

#6


0  

I was able to get Eric Brown - Cal 's solution to work, but I needed a small tweak to prevent an error I was getting about the directory not being found.

我能够让Eric Brown - Cal的解决方案工作,但我需要一个小小的调整来防止我得到关于找不到目录的错误。

(Also, looking at the Rotativa code, it looks like the -q switch is already being passed by default, so that might not be necessary, but I didn't change it.)

(另外,看看Rotativa代码,看起来默认情况下已经传递了-q开关,因此可能没有必要,但我没有更改它。)

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

#1


3  

I have the same problem, here's my solution:

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

You need to basically make an HTTP request to your own URL and save the output as a binary file. Simple, no overload, helper classes, and bloated code.

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

You'll need this method:

你需要这个方法:

    // 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
        { }
    }

Then inside your controller, you call it like this:

然后在你的控制器内,你这样称呼它:

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

And voilà! The file is there on your file system and you can double click and open the PDF, or email it to your users, or whatever you need.

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

#2


1  

 return new Rotativa.ActionAsPdf("ConvertIntoPdf") 
 { 
     FileName = "Test.pdf", PageSize = Rotativa.Options.Size.Letter
 };

#3


0  

Take a look at the MVC pipeline diagram here: http://www.simple-talk.com/content/file.ashx?file=6068

在这里查看MVC管道图:http://www.simple-talk.com/content/file.ashx?file = 6068

The method OnResultExecuted() is called after the ActionResult is rendered.

在呈现ActionResult之后调用OnResultExecuted()方法。

You can override this method or use an ActionFilter to apply and OnResultExecuted interceptor using an attribute.

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

Edit: At the end of this forum thread you will find a reply which gives an example of an ActionFilter which reads (and changes) the response stream of an action. You can then copy the stream to a file, in addition to returning it to your client.

编辑:在此论坛主题的末尾,您将找到一个回复​​,其中提供了一个ActionFilter的示例,该ActionFilter读取(并更改)操作的响应流。然后,您可以将流复制到文件,还可以将其返回到客户端。

#4


0  

I successfully used Aaron's 'SaveHttpResponseAsFile' method, but I had to alter it, as the currently logged in user's credentials weren't applied (and so it was forwarding to MVC4's login url).

我成功使用了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>

Then in your calling Controller method, simply add 'Request' into the SaveHttpResponseAsFile call.

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

#5


0  

You can also do it using Rotativa, which is actually quite easy.

您也可以使用Rotativa来实现,这实际上非常简单。

Using Rotativa;

...

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

File.WriteAllBytes( outputPath, pdfByteArray );

I'm using this in a winforms app, to generate and save the PDFs from Razor Views we also use in our web apps.

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

#6


0  

I was able to get Eric Brown - Cal 's solution to work, but I needed a small tweak to prevent an error I was getting about the directory not being found.

我能够让Eric Brown - Cal的解决方案工作,但我需要一个小小的调整来防止我得到关于找不到目录的错误。

(Also, looking at the Rotativa code, it looks like the -q switch is already being passed by default, so that might not be necessary, but I didn't change it.)

(另外,看看Rotativa代码,看起来默认情况下已经传递了-q开关,因此可能没有必要,但我没有更改它。)

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