Creating a Proxy to Download PDF Reports from SRS






3.40/5 (4 votes)
How to create a proxy that downloads PDF reports from SRS and then stream them back to the client browser
Introduction
The following article describes how you can create a proxy page that converts a SQL Server Report into a PDF file. When the code is executed, it will make a call to a report on a SQL Reporting Services Server, generate a PDF file and then present it to the user as an option download.
A nice benefit to this process is that you never have to save a physical file on the server and then direct the client to that file. Everything is done in memory, so there will be no file permission problems or disk space issues to deal with.
Background
While this code is useful for creating PDF copies of an SRS report, the true value is that you no longer have to grant everyone access to the reports within Reporting Services. To do this, you simply log into the Reporting Services website and set up a single user account that will have access to the reports. From there, be sure to reference this same user account in the credentials when setting up the web request.
This code can also be easily modified to download other types of files from other URLs.
Using the Code
To begin, we must first construct a URI that points to the report on the report server.
string url = "http://localhost/ReportServer/Pages/ReportViewer.aspx?%2fInvoices%2fCurrent&rs%3aCommand=Render&rs%3AFormat=PDF&InvoiceId=1";
Breakdown of the above URL:
http://localhost/ReportServer/Pages/ReportViewer.aspx
- This should point to the ReportViewer page that comes with reporting services.
Invoices
/- This is a folder that you set up in Reporting Services. If your report is in a sub folder, you will need to specify the full path
separated by %2f
Current
- This is the name of your report as seen in reporting services
Command=Render
- This is required to tell the server to generate the report
Format=PDF
- This will render the output as PDF. Reporting Services can render in other formats as well
InvoiceId=1
- This is a custom parameter that gets passed into the report. In this instance, we simply pass id #1
It is possible to create a custom filename for the PDF when it gets downloaded to the client.
// this name is what the user will see when they are prompted for download. string customFileName = "NewFileName.pdf";
This section shows how to set up user credentials to access the Report Server. I
used CredentialCache.DefaultCredentials
for testing on localhost. When
accessing this on a remote server, you may need to pass in user credentials.
// Create a request object that will make a call to the reporting server WebRequest request = WebRequest.Create(url); request.ContentType = @"application/pdf"; // If you require authentication to access your report server, set the variables below string userName = ""; string password = ""; string domain = ""; // If your server requires authentication, you will have to pass them into the request object if (userName.Length > 0) { // Create and then pass in network credentials to acecss the reporting server System.Net.NetworkCredential credentials = new NetworkCredential(userName, password, domain); request.Credentials = credentials; } else // use the default credentials request.Credentials = CredentialCache.DefaultCredentials;
This code captures the response from the Report Server and then saves the results into
a MemoryStream
object so that it can be passed back to the client.
To accomplish this, we call the ReadFully
method to convert the Stream
into a byte array which then gets
fed into the MemoryStream.
Source code for this method can be found at http://www.developerfusion.co.uk/show/4696/ and is also included in the sample project.
MemoryStream ms; // Send a request to download the pdf document and then get the response using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { // Get the stream from the server using (Stream stream = response.GetResponseStream()) { // Use the ReadFully method from the link above: byte[] data = ReadFully(stream, response.ContentLength); // Return the memory stream. ms = new MemoryStream(data); } }
The code for ReadFully can be seen below. This was originally posted at http://www.developerfusion.co.uk/show/4696/
/// Reads data from a stream until the end is reached. The /// data is returned as a byte array. An IOException is /// thrown if any of the underlying IO calls fail. /// /// Original code can be found at http://www.developerfusion.co.uk/show/4696/ public static byte[] ReadFully(Stream stream, long initialLength) { // If we've been passed an unhelpful initial length, just // use 32K. if (initialLength < 1) { initialLength = 32768; } byte[] buffer = new byte[initialLength]; int read = 0; int chunk; while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) { read += chunk; // If we've reached the end of our buffer, check to see if there's // any more information if (read == buffer.Length) { int nextByte = stream.ReadByte(); // End of stream? If so, we're done if (nextByte == -1) { return buffer; } // Nope. Resize the buffer, put in the byte we've just // read, and continue byte[] newBuffer = new byte[buffer.Length * 2]; Array.Copy(buffer, newBuffer, buffer.Length); newBuffer[read] = (byte)nextByte; buffer = newBuffer; read++; } } // Buffer is now too big. Shrink it. byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; }
The final block of code clears out the current Response
object and then
sends it to the client for download. We also add a header to declare a custom filename
from above
and then set the content type to PDF so the browser knows how to handle the file.
The last line of code simply sends the MemoryStream
object to the browser
which then prompts the user for download.
// Clear out the headers that may already exist, then set content type and add a header to name the file Response.Clear(); Response.ContentType = "application/pdf"; Response.AddHeader("content-disposition", "inline; filename=" + customFileName); // Write the memory stream containing the pdf file directly to the Response object that gets sent to the client ms.WriteTo(Response.OutputStream);
Points of Interest
While this example shows how to download a PDF file from SRS, many more formats
are available such as CSV and Excel. To use these formats, locate the Format=PDF
command in the URI and change PDF to either CSV or Excel. You will also have to
update the Response.ContentType
accordingly.
This code uses SQL Reporting Services as an example, however it is not limited to just SRS. You can easily change the URL and alter the content types to access any type of file on the web.