C# .NET - Export Crystal Report to PDF bypassing Report Viewer

Asked By Henry Taylor on 02-May-14 10:22 AM
When I click a link for a Crystal Report I do not want the user to have to wait for the report to open then export it to get the pdf.

Is there a way to have the report export in the background and then show it in the web browser?

I have seen numerous examples on the net but none of them work.

There is always an error on the Export saying there are missing parameters:

    DiskFileDestinationOptions destinationURL = new DiskFileDestinationOptions();
            PdfFormatOptions formatOptionPDF = new PdfFormatOptions();

            destinationURL.DiskFileName = "c:\\" + Session["TFID"].ToString() + ".pdf";

            ExportOptions ex = new ExportOptions();
            ex.ExportDestinationType = ExportDestinationType.DiskFile;
            ex.ExportFormatType = ExportFormatType.PortableDocFormat;
            ex.ExportDestinationOptions = destinationURL;
            ex.ExportFormatOptions = formatOptionPDF;

            ExportRequestContext req = new ExportRequestContext();
            req.ExportInfo = ex;

            Stream ios = (Stream)rptDoc.FormatEngine.ExportToStream(req); <<<<< ERROR
            Response.ClearContent();
            Response.ClearHeaders();
            Response.ContentType = "application/pdf";

            byte[] b = new byte[ios.Length];
            ios.Read(b, 0, (int)ios.Length);
            Response.BinaryWrite(b);
            Response.End();

I have tried numerous variations but always get an error on the Export.

Thanks.

Robbe Morris replied to Henry Taylor on 02-May-14 10:26 AM
Yes, you can export a crystal report as a PDF.  Use the StreamReader class convert it to bytes.  Then, use Response.BinaryWrite() to write the bytes to the browser. 

private static byte[] GetCrystalPDF(ReportDocument rpt)
{
  byte[] buffer = null;
 
  try
  {
 
  using (var sr = new StreamReader(rpt.ExportToStream(ExportFormatType.PortableDocFormat)))
  {
   buffer = new byte[sr.BaseStream.Length];
   sr.BaseStream.Read(buffer, 0, buffer.Length);
   rpt.Close();
   rpt.Dispose();
   rpt = null;
  }
 
  }
  catch (Exception ex)
  {
  // blah
  }
  return buffer;
}

public static void WritePDF(HttpContext context, byte[] bytes)
 {
   if (context == null) return;
   if (bytes == null) return;
 
   context.Response.StatusCode = 200;
   context.Response.ContentType = "application/pdf";
   context.Response.AddHeader("content-length", bytes.Length.ToString());
   context.Response.AddHeader("Content-Disposition", "inline");
   context.Response.BinaryWrite(bytes);
  }
Henry Taylor replied to Robbe Morris on 02-May-14 11:06 AM
Thanks for getting back to me.

On this line:

using (var sr = new StreamReader(rpt.ExportToStream(ExportFormatType.PortableDocFormat)))
I get an error - missing parameters.

at CrystalDecisions.ReportAppServer.ConvertDotNetToErom.ThrowDotNetException(Exception e)
   at CrystalDecisions.ReportSource.EromReportSourceBase.ExportToStream(ExportRequestContext reqContext)
   at CrystalDecisions.CrystalReports.Engine.FormatEngine.ExportToStream(ExportRequestContext reqContext)
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToStream(ExportOptions options)
   at CrystalDecisions.CrystalReports.Engine.ReportDocument.ExportToStream(ExportFormatType formatType)
   at TransportFunding.ssl.ViewCreditApp.GetCrystalPDF(ReportDocument rpt) in C:\Teamwork\TransportFunding\TRansportFunding\ssl\ViewCreditApp.aspx.cs:line 78

It seems like it is almost there.
Robbe Morris replied to Henry Taylor on 02-May-14 11:11 AM
What version of Crystal Reports .NET are you using?

ExportFormatType is an enum.  Do you have PortableDocFormat as an option?

Also, are you sure the error is really about exporting to PDF?  Perhaps the missing parameters message relates to the inability to generate the report because you didn't pass the right parameters to it.  Does the report come up in the reader properly?
Henry Taylor replied to Robbe Morris on 02-May-14 12:16 PM
I have Engine v13.0.2000.0

Let me clear about what I am doing.  The report works - it will open in the viewer but the customer does not want to go to the viewer then export the report then go to the report and open it. When they click the link for the report they just want the pdf file to open in the browser window.

At the end of my code to create the report I put your code there:

protected void Page_Init(object sender, EventArgs e)
        {
            LoadCrystal();
        }

protected void LoadCrystal()
        {
            if (Request.QueryString["ID"] != null)
            {
                Session["ID"] = Request.QueryString["ID"].ToString();
            }

            EncryptEnvData encObj = new EncryptEnvData();
            encObj.GetEnvData();

            ReportDocument rptDoc = new ReportDocument();

            rptDoc.Load(Server.MapPath("\\ssl\\App.rpt"));

            ConnectionInfo connectionInfo = new ConnectionInfo();
            connectionInfo.ServerName = encObj.DataSource;
            connectionInfo.DatabaseName = "F1C";
            connectionInfo.UserID = encObj.AS400ID;
            connectionInfo.Password = encObj.AS400PW;

            SetDBLogonForReport(connectionInfo, rptDoc);

            CrystalReportViewer.ToolPanelView = CrystalDecisions.Web.ToolPanelViewType.None;
            CrystalReportViewer.ReportSource = rptDoc;
            CrystalReportViewer.Zoom(75);
            CrystalReportViewer.HasPrintButton = true;
            CrystalReportViewer.HasCrystalLogo = false;
            CrystalReportViewer.HasToggleGroupTreeButton = false;
            CrystalReportViewer.HasDrillUpButton = false;

            ArrayList arrayList = new ArrayList();
            arrayList.Add(Session["TFID"].ToString());

            ParameterFields parameterFields = CrystalReportViewer.ParameterFieldInfo;

            SetCurrentValuesForParameterField(parameterFields, arrayList);

    //This all works to here and the report shows correctly in the Report viewer
    // Now your stuff

            WritePDF(HttpContext.Current, GetCrystalPDF(rptDoc));   
    // What I want is the report to show but as a PDF file instead of the Crystal viewer
        }

private static byte[] GetCrystalPDF(ReportDocument rpt)
        {
            byte[] buffer = null;
            try
            {
                using (var sr = new StreamReader(rpt.ExportToStream(ExportFormatType.PortableDocFormat)))
                {
                    buffer = new byte[sr.BaseStream.Length];
                    sr.BaseStream.Read(buffer, 0, buffer.Length);
                    rpt.Close();
                    rpt.Dispose();
                    rpt = null;
                }
            }
            catch (Exception ex)
            {
                ex.Message.Remove(0, 1);
            }
            return buffer;
        }

public static void WritePDF(HttpContext context, byte[] bytes)
        {
            if (context == null) return;
            if (bytes == null) return;

            context.Response.StatusCode = 200;
            context.Response.ContentType = "application/pdf";
            context.Response.AddHeader("content-length", bytes.Length.ToString());
            context.Response.AddHeader("Content-Disposition", "inline");
            context.Response.BinaryWrite(bytes);
        }

I now see where the missing parameter error is coming from. The report already has the error on the HasRecords property. That is odd because the report does show correctly in the viewer.
Robbe Morris replied to Henry Taylor on 02-May-14 12:17 PM
I use 11.5 and have a reference to CrystalDecisions.CrystalReports.Engine.dll and CrystalDecisions.Shared.dll.  Outside of that, I can't see where you might be doing anything different than what we are.
Henry Taylor replied to Robbe Morris on 02-May-14 01:47 PM
I found the problem.  My report has one parameter - the ID

I was assigning it like this:

SetCurrentValuesForParameterField(parameterFields, arrayList); << arrayList has the ID

I won't show that sub but it worked well enough to get the report to open.


I saw some code that looped through the keys and check for values:

foreach (ParameterField key in rptDoc.ParameterFields)
            {
                if (!key.HasCurrentValue)
                {
                    Response.Write(key.Name.ToString());
                }
            }

It said the ID was missing.  I added this line of code:

rptDoc.SetParameterValue("ID", Session["ID"].ToString());

Bingo - now the code you gave me works!

And the PDF displays much quicker than the crystal viewer.

I replaced:

SetCurrentValuesForParameterField(parameterFields, arrayList);

with:

rptDoc.SetParameterValue("ID", Session["ID"].ToString());


Thanks for the help Rob.
Robbe Morris replied to Henry Taylor on 02-May-14 02:13 PM
I wondered if that was it.  Good job on working through it.

Be careful with this:  Session["ID"].ToString();

if ID has not been set in session or if the session expires, this will throw an untrapped Exception on the call to .ToString().

On a side note, I also use Adobe PDF to create PDFs server side and send them to the printer automatically.  Most of the PDF control providers don't offer this.  We use it in conjunction with exported PDFs from Crystal Reports.