views:

2660

answers:

3

I am looking to stream a file housed in a SharePoint 2003 document library down to the browser. Basically the idea is to open the file as a stream and then to "write" the file stream to the reponse, specifying the content type and content disposition headers. Content disposition is used to preserve the file name, content type of course to clue the browser about what app to open to view the file.

This works all good and fine in a development environment and UAT environment. However, in the production environment, things do not always work as expected,however only with IE6/IE7. FF works great in all environments.

Note that in the production environment SSL is enabled and generally used. (When SSL is not used in the production environment, file streams, is named as expected, and properly dislays.)

Here is a code snippet:

System.IO.FileStream fs = new System.IO.FileStream(Server.MapPath(".") + "\\" + "test.doc", System.IO.FileMode.Open);
long byteNum = fs.Length;
byte[] pdfBytes = new byte[byteNum];
fs.Read(pdfBytes, 0, (int)byteNum);

Response.AppendHeader("Content-disposition", "filename=Testme.doc");
Response.CacheControl = "no-cache";
Response.ContentType = "application/msword; charset=utf-8";
Response.Expires = -1;
Response.OutputStream.Write(pdfBytes, 0, pdfBytes.Length);
Response.Flush();
Response.Close();
fs.Close();

Like I said, this code snippet works fine on the dev machine and in the UAT environment. A dialog box opens and asks to save, view or cancel Testme.doc. But in production onnly when using SSL, IE 6 & IE7 don't use the name of the attachment. Instead it uses the name of the page that is sending the stream, testheader.apx and then an error is thrown.

IE does provide an advanced setting "Do not save encrypted pages to disk".

I suspect this is part of the problem, the server tells the browser not to cache the file, while IE has the "Do not save encrypted pages to disk" enabled.

Yes I am aware that for larger files, the code snippet above will be a major drag on memory and this implimentation will be problematic. So the real final solution will not open the entire file into a single byte array, but rather will open the file as a stream, and then send the file down to the client in bite size chunks (e.g. perhaps roughly 10K in size).

Anyone else have similar experience "streaming" binary files over ssl? Any suggestions or recommendations?

+2  A: 

It might be something really simple, believe it or not I coded exactly the same thing today, i think the issue might be that the content disposition doesnt tell the browser its an attachment and therefore able to be saved.


Response.AddHeader("Content-Disposition", "attachment;filename=myfile.doc");
failing that i've included my code below as I know that works over https://

private void ReadFile(string URL)
{
  try
  {
                string uristring = URL;
                WebRequest myReq = WebRequest.Create(uristring);
                NetworkCredential netCredential = new NetworkCredential(ConfigurationManager.AppSettings["Username"].ToString(), 
                                                                        ConfigurationManager.AppSettings["Password"].ToString(), 
                                                                        ConfigurationManager.AppSettings["Domain"].ToString());
                myReq.Credentials = netCredential;
                StringBuilder strSource = new StringBuilder("");

                //get the stream of data 
                string contentType = "";
                MemoryStream ms;
                // Send a request to download the pdf document and then get the response
                using (HttpWebResponse response = (HttpWebResponse)myReq.GetResponse())
                {
                    contentType = response.ContentType;
                    // 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);
                    }
                }

                Response.Clear();
                Response.ContentType = contentType;
                Response.AddHeader("Content-Disposition", "attachment;");

                // Write the memory stream containing the pdf file directly to the Response object that gets sent to the client
                ms.WriteTo(Response.OutputStream);
  }
  catch (Exception ex)
  {
    throw new Exception("Error in ReadFile", ex);
  }
}

Mauro
+2  A: 

Ok, I resolved the problem, several factors at play here.

Firstly this support Microsoft article was beneficial: Internet Explorer is unable to open Office documents from an SSL Web site.

In order for Internet Explorer to open documents in Office (or any out-of-process, ActiveX document server), Internet Explorer must save the file to the local cache directory and ask the associated application to load the file by using IPersistFile::Load. If the file is not stored to disk, this operation fails.

When Internet Explorer communicates with a secure Web site through SSL, Internet Explorer enforces any no-cache request. If the header or headers are present, Internet Explorer does not cache the file. Consequently, Office cannot open the file.

Secondly, something earlier in the page processing was causing the "no-cache" header to get written. So Response.ClearHeaders needed to be added, this cleared out the no-cache header, and the output of the page needs to allow caching.

Thirdly for good measure, also added on Response.End, so that no other processing futher on in the request lifetime attempts to clear the headers I've set and re-add the no-cache header.

Fourthly, discovered that content expiration had been enabled in IIS. I've left it enabled at the web site level, but since this one aspx page will serve as a gateway for downloading the files, I disabled it at the download page level.

So here is the code snippet that works (there are a couple other minor changes which I believe are inconsequential):

System.IO.FileStream fs = new System.IO.FileStream(Server.MapPath(".") + "\\" + "TestMe.doc", System.IO.FileMode.Open);
long byteNum = fs.Length;
byte[] fileBytes = new byte[byteNum];
fs.Read(fileBytes, 0, (int)byteNum);

Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader("Content-disposition", "attachment; filename=Testme.doc");
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.ContentType = "application/octet-stream";
Response.OutputStream.Write(fileBytes, 0, fileBytes.Length);
Response.Flush();
Response.Close();
fs.Close();
Response.End();

Keep in mind too, this is just for illustration. The real production code will include exception handling and likely read the file a chunk at a time (perhaps 10K).

Mauro, thanks for catching a detail that was missing from the code as well.

Jon
A: 

Thaanks a lot, This piece of code helped me ms.WriteTo(Response.OutputStream);