【问题标题】:SharePoint stream file for preview用于预览的 SharePoint 流文件
【发布时间】:2010-09-08 08:06:28
【问题描述】:

我希望将 SharePoint 2003 文档库中的文件流式传输到浏览器。基本上,这个想法是将文件作为流打开,然后将文件流“写入”到响应中,指定内容类型和内容处置标头。内容配置用于保存文件名,内容类型当然是为了提示浏览器打开什么应用程序来查看文件。

这在开发环境和 UAT 环境中运行良好。但是,在生产环境中,事情并不总是按预期工作,但仅限于 IE6/IE7。 FF 适用于所有环境。

请注意,在生产环境中启用并通常使用 SSL。 (当生产环境中不使用 SSL 时,文件流,按预期命名,并正确显示。)

这是一个代码sn-p:

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();

就像我说的,这段代码 sn-p 在开发机器和 UAT 环境中运行良好。将打开一个对话框并要求保存、查看或取消 Testme.doc。但仅在生产中使用 SSL 时,IE 6 和 IE7 不使用附件名称。相反,它使用发送流的页面名称 testheader.apx,然后引发错误。

IE 确实提供了高级设置“不将加密页面保存到磁盘”。

我怀疑这是问题的一部分,服务器告诉浏览器不要缓存文件,而 IE 启用了“不要将加密的页面保存到磁盘”。

是的,我知道对于较大的文件,上面的代码 sn-p 将是内存的主要拖累,并且这种实现将是有问题的。所以真正的最终解决方案不会将整个文件打开为单字节数组,而是将文件作为流打开,然后将文件以一口大小的块(例如大约 10K 大小)发送到客户端。

还有其他人有类似的通过 ssl“流式传输”二进制文件的经验吗?有什么建议或建议吗?

【问题讨论】:

  • 抛出的错误是什么?此代码中没有 SharePoint……您当前的生产代码是这样吗?
  • 未知 我代表尚未加入 SO 的同事发布此信息。你是对的,这段代码中没有共享点,这是一个精简的示例。生产代码确实与共享点文档库交互。我应该说得更清楚。

标签: asp.net sharepoint ssl binary streaming


【解决方案1】:

这可能很简单,信不信由你我今天编写的代码完全相同,我认为问题可能是内容配置没有告诉浏览器它是附件,因此可以保存。


Response.AddHeader("Content-Disposition", "attachment;filename=myfile.doc");

我没有在下面包含我的代码,因为我知道它适用于 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); } }

【讨论】:

【解决方案2】:

好的,我解决了这个问题,这里有几个因素在起作用。

首先,这篇支持 Microsoft 的文章是有益的: Internet Explorer is unable to open Office documents from an SSL Web site.

为了让 Internet Explorer 在 Office(或任何进程外的 ActiveX 文档服务器)中打开文档,Internet Explorer 必须将文件保存到本地缓存目录并要求相关的应用程序加载文件通过使用 IPersistFile::Load。如果文件未存储到磁盘,则此操作失败。

当 Internet Explorer 通过 SSL 与安全网站通信时,Internet Explorer 会强制执行任何无缓存请求。如果存在一个或多个标头,Internet Explorer 不会缓存该文件。因此,Office 无法打开该文件。

其次,页面处理的早期部分导致“no-cache”标头被写入。所以需要添加Response.ClearHeaders,这样就清除了no-cache header,页面的输出需要允许缓存。

第三,为了更好地衡量,也添加在 Response.End 上,以便在请求生命周期中没有其他处理尝试清除我设置的标头并重新添加无缓存标头。

第四,发现在 IIS 中启用了内容过期。我已经在网站级别启用了它,但是由于这个 aspx 页面将用作下载文件的网关,所以我在下载页面级别禁用了它。

所以这里是有效的代码 sn-p(我认为还有一些其他的小改动是无关紧要的):

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();

请记住,这只是为了说明。真正的生产代码将包括异常处理,并可能一次读取文件一个块(可能是 10K)。

Mauro,感谢您也发现了代码中缺少的细节。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多