【问题标题】:ASP.NET Inconsistent PDF file download resultsASP.NET 不一致的 PDF 文件下载结果
【发布时间】:2012-10-01 01:35:30
【问题描述】:

我正在开发一个包含树形视图的 ASP.NET (3.5) 网站;节点的值设置为 PDF 文件的文件路径(在服务器上)。当用户点击树节点时,服务器端代码获取节点值(文件路径),创建一个 FileInfo 对象,并(在正确设置所有标头 Content-type、Cache-Control 等后)调用 Response.TransmitFile( xxxxpath) 发送到用户的浏览器。

这在主要浏览器和主要设备(PC、Mac、iOS 设备)上运行良好。该文件正确下载并在用户的机器上打开。但在某些设备和某些浏览器上,无法打开 PDF 文件。在 Android 设备上,Firefox 似乎可以正确下载和打开 PDF,但普通的 Android 浏览器不能。在 Kindle Fire 上,Silk 浏览器似乎成功下载了文件,但是在尝试打开它时,我看到一个错误:“找不到 PDF 预告片”......或者它说 PDF 受 DRM 保护(它是不是)。我还没有在 Fire 上尝试过其他浏览器(如果有的话)。

我已经尝试在静态 HTML 标记中使用锚链接,问题浏览器似乎在以这种方式访问​​时可以正确下载和显示 PDF。当通过代码完成时,ASP.NET 向浏览器发送响应的方式似乎存在问题(不一致?)。我使用过 Response.Flush、Response.WriteFile、Response.BinaryWrite、Response.Close、Response.End 等,它们都产生相同的结果:大多数浏览器处理文件,但有些浏览器无法处理 PDF。

那么,ASP.NET 构造响应对象的方式(尤其是在发回 PDF 时)是否存在一些浏览器不喜欢的问题? TIA。

【问题讨论】:

  • 你找到最终解决方案了吗?如果您尝试,几个 SO 答案 可能会提供更多信息。

标签: asp.net pdf browser download


【解决方案1】:

很简单,您的问题的答案是“否”。如果您对自己的操作是否正确有疑问,您可能需要发布您的代码;否则:“不”。 :)

我认为你提到的浏览器比写入响应流这样简单且成熟的浏览器更令人怀疑。

作为参考,这是我使用 iHttpHandler 的一种经过验证的真实方法:

    public void ProcessRequest(System.Web.HttpContext context)
    {
        string sFilePath = context.Server.MapPath(String.Concat("~/App_LocalResources", context.Request.QueryString["path"]));
        try
        {
            context.Response.Clear();
            context.Response.ContentType = "application/octet-stream";

            int iReadLength = 16384;
            int iLastReadLength = iReadLength;
            byte[] buffer = new byte[iReadLength];
            if (System.IO.File.Exists(sFilePath))
            {
                System.IO.FileInfo fInfo = new System.IO.FileInfo(sFilePath);
                context.Response.AddHeader("Content-Length", fInfo.Length.ToString());
                context.Response.AddHeader("Content-Disposition", String.Format("attachment; filename=\"{0}\"", fInfo.Name));
                using (System.IO.FileStream input = new System.IO.FileStream(sFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                {
                    try
                    {
                        while (iLastReadLength > 0 && context.Response.IsClientConnected)
                        {
                            iLastReadLength = input.Read(buffer, 0, iLastReadLength);

                            if (iLastReadLength > 0)
                            {
                                context.Response.OutputStream.Write(buffer, 0, iLastReadLength);
                                context.Response.OutputStream.Flush();
                            }
                        }
                        context.Response.Flush();
                    }
                    catch
                    {
                    }
                    finally
                    {
                        input.Close();
                    }
                }
            }
        }
        catch
        {
        }
        finally
        {
        }
    }

既然您已经表明您是从另一个地方提取文件,那么这里是如何将其写入内存流的方法。只需从远程服务器(或 File Stream、Sql Binary Reader,实际上)拉取响应流,然后重置 MemoryStream 位置,然后使用上面的功能将响应流写入客户端。

int iReadLength = 16384;
long lLastReadLength = iReadLength;
long lDataIndex = 0;
byte[] buffer = new byte[iReadLength];
using (System.IO.MemoryStream msTemp = new System.IO.MemoryStream())
{
    while (lLastReadLength > 0)
    {
        lLastReadLength = reader.GetBytes(0, lDataIndex, buffer, 0, iReadLength);
        lDataIndex += lLastReadLength;

        if (lLastReadLength > 0)
        {
            msTemp.Write(buffer, 0, Convert.ToInt32(lLastReadLength));
            msTemp.Flush();
        }
    }

    // Reset Memory Position
    msTemp.Position = 0;

    // Now write to the Response Stream here
}

【讨论】:

  • 我真的被困住了。我使用了你的代码(我没有使用 HttpHandler,否则代码是完整的),结果相同。我尝试了十几种不同的代码方法,尽管它们都是通过 Response 将文件发送回浏览器的变体。如果他们最初无法下载文件,我准备认输并告诉人们使用 Firefox。如果我的任何同事不喜欢它,他们可以自己提出解决方案!感谢您提供代码和信息。
  • 不使用处理程序的唯一一点是,您需要在将所有内容写入响应流之后调用 Response.End()(在我的示例中,就在 finally 块之后)。否则 .aspx 页面可能会向浏览器发送更多可能破坏它的内容。调用 Response.End 从来都不是一件有趣的事,因为当你这样做时避免“线程被中止”错误真的很痛苦。
  • 哦,我已经使用了 Response.End、Response.Close 和许多其他命令组合(并且只是捕获 ThreadAbort 异常),但仍然没有骰子。该站点设置中唯一的其他“奇怪之处”是链接到(在页面上)的文件驻留在与 Web 服务器不同的服务器上。在服务器端代码中,我使用 UNC 名称来检索 PDF,然后通过 Response 将其发回。它们不是直接链接 (www.mysite.com/pubs/mypdf.pdf)。你认为有没有可能因为我的文件必须由服务器检索,服务器正在发送一些带有字节数组的附加信息?
  • 是的,这是一个有趣的细节。您是将传入的文件保存到 MemoryStream 还是在用它做什么?
  • 好吧,我将流定义为 Response.OutputStream,在获得文件字节后,我执行 stream.Write(),然后执行 Response.Flush()(正如我所说,各种其他写入/刷新/结束/关闭/等)。我现在在想 ASP.NET TreeView 可能存在一些问题(可能与 Javascript 相关)。我知道如果我将节点的 NavigateUrl 直接设置为 UNC 文件路径,IE 会正常处理文件,但 Firefox 会崩溃并显示“错误请求”。
猜你喜欢
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-09-06
  • 2021-12-17
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
相关资源
最近更新 更多