Successfully Stream a PDF to browser through HTTPS

May 28, 2007

In this article I will try to explain you the technique of displaying file in the browser using Byte Array through HTTPS. Reason behind writing this article is, I need to implement this functionality in my application I invested lots of hours behind this and finally I found the solution.

Problem Description

I have a popup window in which to display a PDF file. This PDF is coming from Web Service call, which returns us a Byte Array. All ok so far, byte array is populated.

Byte Array is passed back as a MemoryStream. That MemoryStream is then sent out through ASP.Net’s Response object after setting the ContentType=”application/pdf”.

In development, I had no problems using the following code with HTTP:

Response.Clear();

//Send the file to the output stream

Response.Buffer = true;

//Try and ensure the browser always opens the file and doesn’t just prompt to “open/save”.

Response.AddHeader(“Content-Length”, ByteArray.Length.ToString());

Response.AddHeader(“Content-Disposition”, “inline; filename=” + PDFName);

Response.AddHeader(“Expires”,”0″);

Response.AddHeader(“Pragma”,”cache”);

Response.AddHeader(“Cache-Control”,”private”);

//Set the output stream to the correct content type (PDF).

Response.ContentType = “application/pdf”;

//Output the file

Response.BinaryWrite(ByteArray);

//Flushing the Response to display the serialized data

//to the client browser.

Response.Flush();

try{Response.End();}

catch{}

Everything seems fine until I moved it to our integration server which has HTTPS enabled.

Suddenly, I’m getting a dialog box stating “This page contains both secure and nonsecure items. Do you want to display the nonsecure items?” Clicking either Yes or No continued to display the document as normal, but there seemed no solution to getting rid of the dialog box.

If you do a Google on this text, you’ll only get back about four pages and one Groups post. Do enough digging and you’ll eventually find the following two MSDN articles:
http://support.microsoft.com/default.aspx?scid=kb;en-us;300443
http://support.microsoft.com/?kbid=321532

The second post leads you to the solution. Notice that the code above includes the length of the byte array being written to the output stream? I did, so I believed that ASP.Net would correctly populate the HTTP Headers indicating the length of the output. Unfortunately, this was a bad assumption. It turns out that IE6 requires the use of the “Accept-Ranges” HTTP header when HTTPS is used. Microsoft even acknowledges that there may be a bug in IE6. Since I am navigating to the PDF display page using GET and not POST, I can assume from the KB article that a POST wouldn’t work even after adding the “Accept-Ranges” HTTP Header.

Here’s the corrected code:

Response.Clear();

//Send the file to the output stream

Response.Buffer = true;

Response.AddHeader(“Accept-Header”,resultSet.Document.Length.ToString());

//Try and ensure the browser always opens the file and doesn’t just prompt to “open/save”.

Response.AddHeader(“Content-Length”, ByteArray.Length.ToString());

Response.AddHeader(“Content-Disposition”, “inline; filename=” + PDFName);

Response.AddHeader(“Expires”,”0″);

Response.AddHeader(“Pragma”,”cache”);

Response.AddHeader(“Cache-Control”,”private”);

//Set the output stream to the correct content type (PDF).

Response.ContentType = “application/pdf”;

Response.AddHeader(“Accept-Ranges”, “bytes”);

//Output the file

Response.BinaryWrite(ByteArray);

//Flushing the Response to display the serialized data

//to the client browser.

Response.Flush();

try{Response.End();}

catch{}

References:

http://blogs.ittoolbox.com/visualbasic/dotnet/archives/successfully-stream-a-pdf-through-https-1554

If you are looking for detail information on Content-Disposition Header, you can visit following link.

http://www.imc.org/ietf-822/old-archive1/msg03620.html

Advertisements

The underlying connection was closed: Unable to connect to the remote server.

March 1, 2007

we were in the technical problem since since last couple of the days. we googled it a lot , however after all we have done it today :). special thanks to Jigar Pandit for the same.

Problem:
Web service call is working fine in our local system. however when we installed middle-tier service (which contains dll of web-service call) on remote server. it started giving “The underlying connection was closed: Unable to connect to the remote server.” Error. 

Description
This problem may occur when one or more of the following conditions are true:

A network outage occurs.
A proxy server blocks the HTTP request.
A Domain Name System (DNS) problem occurs.
A network authentication problem occurs.

Resolution

To resolve this problem, make sure that the proxy settings are correct. To do this, use the following techniques:

Use the static WebProxy.GetDefaultProxy method. For more information, visit the following Microsoft Web site:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetwebproxyclassgetdefaultproxytopic.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetwebproxyclassgetdefaultproxytopic.asp)

Define a <proxy> element in the application configuration files. For more information, visit the following Microsoft Web site:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfproxyelement.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfproxyelement.asp)

Configure the .NET client to use HTTP 1.0 by changing the HttpWebRequest.ProtocolVersion property. For more information, visit the following Microsoft Web site:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnethttpwebrequestclassprotocolversiontopic.asp (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnethttpwebrequestclassprotocolversiontopic.asp)

Note By default, the .NET Framework uses HTTP 1.1.

Out of this 3 option, first one works for us.

Reference Taken From
http://support.microsoft.com/kb/915599/en-us


Object Array to DataSet

February 21, 2007

I’m working with a Web Service right now that returns an array of objects. What I want to do, is bind the Object Array to a DataGrid. I was able to convert the Object Array to a DataSet and then bind it to the DataGrid. 

Code Snippet

using System;
using System.Data;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
 
 
namespace BusinessObject.Util
{
    /// <summary>
    /// Utility Class for Object Arrays.
    /// </summary>
    public class ObjectArray
    {
        private Object[] _objectArray;
 
        public ObjectArray(Object[] objectArray)
        {
            this._objectArray = objectArray;
        }
 
        public DataSet ToDataSet()
        {
            DataSet ds = new DataSet();
 
            XmlSerializer xmlSerializer = 
		new XmlSerializer(_objectArray.GetType());
            StringWriter writer = new StringWriter();
            xmlSerializer.Serialize(writer, _objectArray);
            StringReader reader = 
		new StringReader(writer.ToString());
 
            ds.ReadXml(reader);
            return ds;
        }
    }
}

How to Use the ObjectArray Class with a Web Service

I would think that it’s obvious how this class should be used, but there’s always a chance that the person reading this article is new to .NET and could use an explanation.

Save the class to a folder in your project and change the Namespace of the class to match the location in your project.

Let’s say that the Web Service provided you with an array of “Persons” ( Person [ ] ). Create a new Person Array:

Person[] personList;

Call the Web Service method that returns the Person Array:

personList = Course.GetPersonList();

Instantiate the downloaded “Object Array” class with the personList:

ObjectArray objectArray = new ObjectArray(personList);

Create a DataSet and convert your ObjectArray to a DataSet using the method:

DataSet ds = new DataSet(); 
ds = objectArray.ToDataSet();

Reference Taken From
http://tom.gilki.org/programming/net/Utils/ObjectArrayDataSet/index.shtml