ASP.NET PDF文件下载结果不一致

ASP.NET PDF文件下载结果不一致,asp.net,pdf,browser,download,Asp.net,Pdf,Browser,Download,我正在开发一个ASP.NET(3.5)网站,其中包含一个treeview;节点的值设置为PDF文件的文件路径(在服务器上)。当用户单击树节点时,服务器端代码获取节点值(文件路径),创建FileInfo对象,并(在正确设置所有标题内容类型、缓存控制等后)调用Response.TransmitFile(xxxxpath)发送到用户的浏览器 这在主要浏览器和主要设备(PC、Mac、iOS设备)上运行良好。文件正确下载并在用户计算机上打开。但在某些设备和浏览器上,PDF文件不会打开。在Android设备

我正在开发一个ASP.NET(3.5)网站,其中包含一个treeview;节点的值设置为PDF文件的文件路径(在服务器上)。当用户单击树节点时,服务器端代码获取节点值(文件路径),创建FileInfo对象,并(在正确设置所有标题内容类型、缓存控制等后)调用Response.TransmitFile(xxxxpath)发送到用户的浏览器

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

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


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

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

我认为,您提到的浏览器比像写入响应流这样简单而成熟的东西更值得怀疑

以下是我使用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
        {
        }
    }
由于您已经指出要从另一个地方提取文件,下面介绍如何将其写入内存流。只需从远程服务器(或文件流、Sql二进制读取器、w/e)中提取响应流,然后重置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
}

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

我认为,您提到的浏览器比像写入响应流这样简单而成熟的东西更值得怀疑

以下是我使用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
        {
        }
    }
由于您已经指出要从另一个地方提取文件,下面介绍如何将其写入内存流。只需从远程服务器(或文件流、Sql二进制读取器、w/e)中提取响应流,然后重置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,但在其他方面代码是完整的),得到了相同的结果。我尝试了十几种不同的代码方法,尽管它们都是通过响应将文件发送回浏览器的变体。我准备认输了,告诉人们如果一开始不能下载文件,就使用Firefox。如果我的同事不喜欢,他们可以自己想出解决办法!感谢您提供的代码和信息。不使用处理程序的唯一一点是,在将所有内容写入响应流之后(在我的示例中,就在finally块之后),您需要调用Response.End()。否则,aspx页面可能会向浏览器发送更多内容,从而导致浏览器崩溃。调用Response.End从来都不是一件有趣的事情,因为这样做时要避免“线程被中止”错误是一件非常痛苦的事情。哦,我使用了Response.End、Response.Close和许多其他命令组合(以及捕捉ThreadAbort异常),但仍然没有成功。该站点设置中唯一的另一个“奇怪之处”是链接到(页面上)的文件位于与web服务器不同的服务器上。在服务器端代码中,我使用UNC名称检索PDF,然后通过响应将其发送回。它们不是直接链接(www.mysite.com/pubs/mypdf.pdf)。您认为有没有可能因为我的文件必须由服务器检索,服务器会用字节数组发送一些附加信息?是的,这是一个有趣的细节。您是在将传入文件保存到MemoryStream还是在对其进行处理?嗯,我将流定义为Response.OutputStream,在获得文件的字节后,我将执行stream.Write(),然后执行Response.Flush()(如我所说,还有各种其他写入/刷新/结束/关闭/等等)。我现在想ASP.NETTreeView可能存在一些问题(可能与Javascript相关)。我知道如果我将一个节点的NavigateUrl直接设置为UNC文件路径,IE可以处理该文件,但Firefox崩溃了,说“请求不好,我真的卡住了。”。我使用了您的代码(我没有使用HttpHandler,但在其他方面代码是完整的),得到了相同的结果。我尝试了十几种不同的代码方法,尽管它们都是通过响应将文件发送回浏览器的变体。我准备认输了,告诉人们如果一开始不能下载文件,就使用Firefox。如果我的同事不喜欢,他们可以自己想出解决办法!感谢您提供的代码和信息。不使用处理程序的唯一一点是,在将所有内容写入响应流之后(在我的示例中,就在finally块之后),您需要调用Response.End()。否则,aspx页面可能会向浏览器发送更多内容,从而导致浏览器崩溃。和呼叫应答