C# MVC在安卓Chrome上下载PDF失败

3

第一次在这里提问,如果我做错了,请告诉我......

我的C# MVC5应用程序允许用户下载PDF文件。它工作得很好,除了在我的Android手机上。我正在使用Chrome,并让操作系统处理PDF。看起来它正在使用Google Drive应用程序内置的PDF查看器。

当我使用直接链接到该文件时,它正常工作,但是当我通过控制器操作下载它时,会出现“文件格式无效”的错误。我将其简化为此问题的测试案例。

所以这是我的操作方法:

public ActionResult IndirectTestFile()
{
string filename = "document.pdf";

var disposition = new System.Net.Mime.ContentDisposition("inline") { FileName = filename };
Response.AppendHeader("Content-Disposition", disposition.ToString());

string serverFilename = Server.MapPath("~/files/direct/" + filename);
return File(serverFilename, "application/pdf");
}

并且我的HTML代码是:
//This fails:
<a href="/report/indirectTestFile">Indirect link to file</a>

//This works:
<a href="/Files/direct/Document.pdf">Direct link to file</a>

我使用Windows(桌面)Chrome获取这些请求的标头。
以下是直接链接的响应标头:
HTTP/1.1 200 OK
Content-Type: application/pdf
Last-Modified: Thu, 07 Jul 2016 11:46:47 GMT
Accept-Ranges: bytes
ETag: "b8a3dd3d45d8d11:0"
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Thu, 07 Jul 2016 12:33:05 GMT
Content-Length: 51793

并且链接到MVC操作的响应头:

HTTP/1.1 200 OK
Cache-Control: private, s-maxage=0
Content-Type: application/pdf
Server: Microsoft-IIS/10.0
Content-Disposition: inline; filename=document.pdf
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 07 Jul 2016 12:34:09 GMT
Content-Length: 51793

我没有看到任何显著的差异,但一个可靠地工作,另一个可靠地失败。

这不是下载损坏了。如果我在Windows上下载“坏”的PDF文件并将其复制到Android手机上,同样的查看器可以正常显示它!

有人以前遇到过这种情况吗?谢谢任何帮助。

2个回答

1

给未来的搜索者:

我还没有解决这个问题,但我确切地知道了原因。查看我的日志后,我发现是 Google Drive/Docs 应用程序处理下载而不是浏览器。该请求使用与浏览器不同的用户代理发出。

这意味着它发生在不同的会话中。该会话未登录到网站,因此它不会下载 PDF 文件,而是重定向到登录页面并下载该页面,并抱怨它不是有效的 PDF 文件!这就是为什么会发生这种情况。

我通过从控制器中删除身份验证来进行测试,因此该请求不会被重定向,PDF 下载并正常显示。

我无法弄清楚为什么 Chrome 自己处理直接下载,却将控制器链接传递给 Google 应用程序。我更改了我的服务器,使两个请求返回相同的标头,所以我不知道 Chrome 如何区分它们并对其进行不同的处理,但它确实做到了。

我需要弄清楚这一点或重新组织我的应用程序以解决此问题,但也许这是另一个问题。

感谢 Fran 提供的想法。

更新:我不记得确切的解决方案(很遗憾,只有几周前...),但我认为是因为我重定向到了我的控制器动作。例如,我会有一个简化版的动作如下:

    public ActionResult MakePDF(string id)
    {
        // code to create the file removed for clarity.
        return RedirectToAction("IndirectTestFile");
    }

这会生成PDF文件并重定向到返回给用户的操作。因此,我像这样切掉了重定向。
    public ActionResult MakePDF(string id)
    {
        // code to create the file removed for clarity.
        return IndirectTestFile();
    }

看起来这就是解决方案——我无论如何都正在使用这种方法,它对我有用。


我也遇到了完全相同的问题。如果你找到了任何解决方法,能否更新一下?我也会继续研究的。 - Danation
看起来这是由Content-Disposition头引起的。我尝试了各种方法使其工作,但运气不太好。如果您不尝试将其内联,Chrome会直接下载它。 - Danation
我在Content-Disposition中看到的唯一区别是,如果你将它设置为attachment,你就是在告诉chrome下载它,所以它会自动下载,因此不需要将它传递给Google Drive应用程序。 - EamonnM
@Danation - 我为你添加了上面的更新。我猜如果安卓Chrome看到一个PDF链接,它会下载它,但如果它看到一个PDF重定向,它会将其交出?这与我的原始帖子不太合理,但这是我现在的看法。 - EamonnM

0

当我编写PDF时,我使用自定义的PdfResult

public class PdfResult : FileResult
{
    private const String DefaultFileName = "file.pdf";
    private readonly Byte[] _byteArray;

    public PdfResult(Byte[] byteArray, String fileName = DefaultFileName)
        : base(MediaTypeNames.Application.Pdf)
    {
        _byteArray = byteArray;
        FileDownloadName = fileName;
    }

    protected override void WriteFile(HttpResponseBase response) { response.BinaryWrite(_byteArray); }
}

我会更改您的控制器操作,使用PdfResult,将文件的字节传递进去。您可以通过使用File.ReadAllBytes获取字节。
public ActionResult IndirectTestFile()
{
string filename = "document.pdf";

var fileBytes = File.ReadAllBytes(Server.MapPath("~/files/direct/" + filename));
return new PdfResult(fileBytes, filename);
}

你的第二个链接

<a href="/Files/direct/Document.pdf">Direct link to file</a>

这个方法有效是因为浏览器直接读取文件。


嗨Fran,我尝试了你的代码,但无法确定它是否有效,因为它使用content-disposition: attachment来强制下载。下载功能正常,可以查看文件,但不能直接在页面中显示。这个代码能否修改为以内联方式而不是附件形式呢?我不确定这里有什么区别,除了以不同的方式将文件加载到流中之外。 - EamonnM
你说浏览器正在直接下载第二个链接 - 就我所理解的,浏览器以完全相同的方式下载这两个链接。无论是浏览器还是PDF查看器都会下载它们。浏览器下载一个链接但PDF查看器却下载另一个链接是没有意义的。也许我错了? - EamonnM
我的意思是你的第一个链接把asp.net mvc放在了中间。一个带有href到确切文件的锚标签不会通过控制器动作。因此,a标签将被浏览器解释为a标签。 - Fran
你是否真的需要通过控制器动作来完成?为什么不直接提供文件链接呢? - Fran
你看过这个吗?https://productforums.google.com/forum/#!topic/drive/xkkVSoLSIxo - Fran
看起来确实是同样的问题,但他们没有解决方案。不过这让我查看了我的日志,我想我找到了问题所在。 我也遇到了这个问题。看起来是谷歌云盘正在处理下载而不是浏览器(不同的用户代理)。这意味着它发生在不同的会话中。该会话未登录到网站,因此它不是下载PDF文件,而是重定向到登录页面并下载该页面,并抱怨它不是有效的PDF文件!所以这就是为什么会发生这种情况,但我该如何解决?我不能允许未经身份验证的下载。(沮丧) - EamonnM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接