I am using Rotativa tool to display pdf. It works

2019-01-20 04:51发布

问题:

I am using Rotativa tool to display PDF. It works fine on localhost, But does not work on Azure platform.

Below is my code...

public ActionResult GeneratePDF(int id = 0)
    {
        ReportTransactionData reporttransactiondata = db.ReportTransactionDatas.Find(id);
        var viewFileToPrint = @"~/Views/ReportTranData/PDFReport.cshtml";
        //var oRotativaPDF = new Rotativa.ViewAsPdf();
        var oRotativaPDF = new Rotativa.PartialViewAsPdf();
        try
        {
            if (reporttransactiondata == null)
            {
                return HttpNotFound();
            }
            else
            {
                // Populate reporttransactiondata with Verfier Name...TO BE IMPLEMENTED LATER...
                //reporttransactiondata.VerifierName = GetVerifierNameByID(reporttransactiondata.VerifierID);
            }

            // Code to call a function/action...
            //return new Rotativa.ActionAsPdf("PrintRptInPDF", reporttransactiondata) 

            //oRotativaPDF = new Rotativa.ViewAsPdf(viewFileToPrint, reporttransactiondata)
            //        {
            //            FileName = "Technician Job Report.pdf",
            //            PageSize = Size.A4,
            //            PageOrientation = Orientation.Portrait,
            //            PageMargins = new Margins(0, 0, 0, 0),
            //            PageWidth = 230,      //250      //300  // 350
            //            PageHeight = 360,      // 380   // 400 //420  // 450
            //            CustomSwitches = "--disable-smart-shrinking"
            //        };

            oRotativaPDF = new Rotativa.PartialViewAsPdf(viewFileToPrint, reporttransactiondata)
            {
                FileName = "Technician Job Report.pdf",
                PageSize = Size.A4,
                PageOrientation = Orientation.Portrait,
                PageMargins = new Margins(0, 0, 0, 0),
                PageWidth = 230,      //250      //300  // 350
                PageHeight = 360,      // 380   // 400 //420  // 450
                CustomSwitches = "--disable-smart-shrinking"
            };
        }
        catch (Exception ex)
        {
            // TODO: Code here...
        }

        return oRotativaPDF;
    }

Please ignore the commented code. This works just fine but when I deploy my web application, the PDF file is not downloaded at client side and after some time my IE browser display 500 internal server error.

I explored the issue further and came to know that this error may be because wkhtmltopdf.exe does not execute on its own on Azure platform. So I came with the following solution with the help of some search on the net about the issue resolution...

public ActionResult GeneratePDF(int id = 0) { ReportTransactionData reporttransactiondata = db.ReportTransactionDatas.Find(id); string viewName = @"~/Views/ReportTranData/PDFReport.cshtml"; string wkhtmltopdfPath = Server.MapPath(@"~/Rotativa/"); string switches = string.Empty; try { if (reporttransactiondata == null) { return HttpNotFound(); }

            string fullPath = Server.MapPath(@"~/ApplicationFiles/TechnicianJobReport.pdf");
            FileInfo objFileInfo = new System.IO.FileInfo(fullPath);
            if (objFileInfo.Exists)
            {
                objFileInfo.Delete();
            }

            string sViewString = RenderRazorViewToString(viewName, reporttransactiondata);
            var byteArray = ConvertHTMLtoPDF(wkhtmltopdfPath, switches, sViewString);
            var fileStream = new FileStream(fullPath, FileMode.Create, FileAccess.Write);
            fileStream.Write(byteArray, 0, byteArray.Length);
            fileStream.Close();

            // Download file at client side...
            Response.Clear();
            Response.ClearContent();
            Response.ClearHeaders();
            Response.Charset = "UTF-8";
            Response.ContentEncoding = Encoding.UTF8;
            Response.AddHeader("Content-Disposition", "attachment; filename=" + Server.UrlEncode(objFileInfo.Name));
            Response.ContentType = "application/pdf";
            Response.WriteFile(objFileInfo.FullName);
            Response.End();

        }
        catch (Exception ex)
        {
            // Handle exception here and Log Error to file...
            Repositories.Repository objRepository = new Repositories.Repository();
            string sLogFilePath = Server.MapPath(@"~/ApplicationFiles/ErrorLogFile.txt");
            objRepository.LogErrorToFile(ex, sLogFilePath, this.ControllerContext.Controller.ToString());
        }

        return View(reporttransactiondata);
    }
    public string RenderRazorViewToString(string viewName, object model)
    {
        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,viewName);
            var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }

    /// <summary>
    /// Converts given URL or HTML string to PDF.
    /// </summary>
    /// <param name="wkhtmltopdfPath">Path to wkthmltopdf.</param>
    /// <param name="switches">Switches that will be passed to wkhtmltopdf binary.</param>
    /// <param name="html">String containing HTML code that should be converted to PDF.</param>
    /// <returns>PDF as byte array.</returns>
    private static byte[] ConvertHTMLtoPDF(string wkhtmltopdfPath, string switches, string html)
    {
        // switches:
        //     "-q"  - silent output, only errors - no progress messages
        //     " -"  - switch output to stdout
        //     "- -" - switch input to stdin and output to stdout
        switches = "-q " + switches + " -";

        // generate PDF from given HTML string, not from URL
        if (!string.IsNullOrEmpty(html))
            switches += " -";

        var proc = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = Path.Combine(wkhtmltopdfPath, "wkhtmltopdf.exe"),
                Arguments = switches,
                UseShellExecute = false,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                WorkingDirectory = wkhtmltopdfPath,
                CreateNoWindow = true
            }
        };
        proc.Start();

        // generate PDF from given HTML string, not from URL
        if (!string.IsNullOrEmpty(html))
        {
            using (var sIn = proc.StandardInput)
            {
                sIn.WriteLine(html);
            }
        }

        var ms = new MemoryStream();
        using (var sOut = proc.StandardOutput.BaseStream)
        {
            byte[] buffer = new byte[4096];
            int read;

            while ((read = sOut.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
        }

        string error = proc.StandardError.ReadToEnd();

        if (ms.Length == 0)
        {
            throw new Exception(error);
        }

        proc.WaitForExit();

        return ms.ToArray();
    }

But this again works fine on localhost but not on Azure server and gives the same 500 internal server error with no exception at all. Please see if anyone can help here. I am using this wkhtmltopdf exe because I can specify the height and width of the pdf according to my (half of A4 page size) paper size requirement. If there is any other option where I may not end up in the issue of executing an external exe or dll, please suggest that option too.

回答1:

Like (#Fabrizio Accatino) wrote: Rotativa is running wkhtmltopdf.exe. It is located in "Rotativa" folder under the root of your project. So the issue could be:

  1. During the deployment - the Rotativa folder is not created (ensure that you've added the folder and the .exe in the project and set the property of the file as "copy always").
  2. There are missing libraries on the server (ensure that there are presented on the server - msvcp120.dll and msvcr120.dll /under sysWOW64 folder/)
  3. Ensure that the app pool user have the needed permissions to run the executable and to store the temp .pdf file.
  4. Ensure that the path name does not exceed the max length (250 digits I think).

I hope this will guide you for solving the problem.



回答2:

I had the same problem, I managed to solve just by upgrading to the 1.6.3 version of rotating. Follow the link nuget: [https://www.nuget.org/packages/Rotativa/1.6.3] [1]


In the package Manager Console, type:

  1. Uninstall-Package Rotating press ENTER.
  2. Install-Package Rotating-Version 1.6.3 press ENTER.
  3. Check in the rotating folder, if you are now just the file * * wkhtmltopdf. exe * *.

Remark

In the 1.6.3 version the rotating no longer uses the files:

  1. libeay32. dll
  2. LIBGCC_S_DW2-1. dll
  3. MINGWM10. dll
  4. README. txt
  5. ssleay32. dll

It uses only the following file:

  1. wkhtmltopdf. exe

However, when upgrading to this new version, the rotating updates only this file wkhtmltopdf.exe to the new one, replacing it.

Now just rotate it again and it will work again.

[1]: Rotating upgrading to the 1.6.3 version



回答3:

I think only premium accounts on azure allow to run wkhtmltopdf.exe on server or you can also create rotative pdf using this https://rotativahq.com/

Hope this help anyone !