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.

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.

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.

Any ideas?

Thanks!

Matt

有帮助吗?

解决方案

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.

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.

其他提示

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

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

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

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

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.

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).

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.

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

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.

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.

(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.)

var bytes = Rotativa.WkhtmltopdfDriver.ConvertHtml(Server.MapPath(@"/Rotativa"), "-q", html);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top