如何避免使用MVC3中的FileContentResult出现重复的content-disposition头信息?

12

我们在SQL数据库中存储了一些文件。在一个ASP.NET MVC3表单上,我们展示了两个链接:

查看此文件 | 下载此文件

这些链接指向对应的操作方法。下载正常工作--点击链接会在浏览器中弹出保存对话框。然而,显示会导致重复的Content-Disposition头被发送到浏览器,导致Chrome上出现错误,Firefox上则是空白页。

[ActionName("display-file")]
public virtual ActionResult DisplayFile (Guid fileId, string fileName)
{
    var file = _repos.GetFileInfo(fileId);
    if (file != null)
    {
        Response.AddHeader("Content-Disposition", 
            string.Format("inline; filename={0}", file.Name));
        return File(file.Content, file.MimeType, file.Name);
    }
}

[ActionName("download-file")]
public virtual ActionResult DownloadFile (Guid fileId, string fileName)
{
    var file = _repos.GetFileInfo(fileId);
    if (file != null)
    {
        return File(file.Content, file.MimeType, file.Name);
    }
}

以下是发送到浏览器以进行显示操作的2个标头:

Content-Disposition: inline; filename=name-of-my-file.pdf
Content-Disposition: attachment; filename="name-of-my-file.pdf"

我尝试将自定义的content-disposition标头更改为用双引号包含文件名,但它仍向浏览器发送了2个标头。 我还尝试在添加自定义标头之前删除Content-Disposition标头,但似乎attachment标头是在返回FileContentResult之后添加的。

这段代码以前可以工作。 我昨天进行了测试,发现它不再在Chrome或Firefox中工作。 这可能是由于浏览器更新导致的。 IE8和Safari仍然可以正确打开文件。

更新

再次感谢Darin,你是正确的。 我们实际上使用了这种方法,因为我们参考了你回答过的另一个问题

关于如何最终解决我们的问题,我们为显示文件链接设置了自定义路由:

context.MapRoute(null,
    "path/to/display-file-attachment/{fileId}/{fileName}",
    new
    {
        area = "AreaName",
        controller = "ControllerName",
        action = "DisplayFile",
    }
);

页面上的超链接通过路由参数将文件名传递给操作方法,因此它已经成为URL的一部分。因此,在用户决定下载它时(通过单击浏览器PDF查看器中的保存图标),我们不需要添加自定义的内容协商头来使文件名与系统的匹配。所以我们只使用了这个:

[ActionName("display-file")]
public virtual ActionResult DisplayFile (Guid fileId, string fileName)
{
    var file = _repos.GetFileInfo(fileId);
    if (file != null)
    {
        // no custom content-disposition header, and no 3rd fileName argument
        return File(file.Content, file.MimeType);
    }
}

Firefox和Chrome在处理Content-Disposition头方面确实变得更加严格了。 - Julian Reschke
@JulianReschke,您能否详细说明一下非ASCII字符?我们还没有在文件名中测试过Unicode字符。 - danludwig
为了使C-D中文件名中的非ASCII字符在所有浏览器中“正常工作”,服务器目前需要进行用户代理嗅探。请参见http://greenbytes.de/tech/tc2231/和http://greenbytes.de/tech/webdav/rfc6266.html。我对ASP.net是否正确处理此问题表示怀疑,但我很想找出答案。 - Julian Reschke
1个回答

29

如果您使用重载函数File(byte[] contents, string mimeType, string fileName),则会自动将Content-Disposition标头添加到响应中,并设置为attachment,因此您不需要再次添加它。对于inline,您可以使用以下重载函数File(byte[] contents, string mimeType)手动添加Content-Disposition标头:

[ActionName("display-file")]
public virtual ActionResult DisplayFile(Guid fileId)
{
    var file = _repos.GetFileInfo(fileId);
    var cd = new ContentDisposition
    {
        Inline = true,
        FileName = file.Name
    };
    Response.AddHeader("Content-Disposition", cd.ToString()); 
    return File(file.Content, file.MimeType);
}

[ActionName("download-file")]
public virtual ActionResult DownloadFile(Guid fileId)
{
    var file = _repos.GetFileInfo(fileId);
    return File(file.Content, file.MimeType, file.Name);
}

你比我快,我正要发布类似的答案。省略第三个fileName参数即可。然而,在我们的情况下,不需要添加content-disposition。只需使用return File(file.Content, file.MimeType);即可,因为文件名是从自定义路由派生的。 - danludwig
希望这个框架在处理非ASCII字符方面能做正确的事情... - Julian Reschke
达林:在除了Safari以外的所有现代浏览器中,它的工作方式都是相同的。 - Julian Reschke
我认为自己很擅长我的工作,直到我看到@DarinDimitrov无处不在! - Fabio Milheiro
是的 - 很好的解释和很好的解决方案,这种类型的答案使stackoverflow成为它所是的样子。 - Terrance00
显示剩余3条评论

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