如何使用 MVC3 FileContentResult 避免重复的内容处置标头?

发布于 2024-12-22 18:47:37 字数 2100 浏览 1 评论 0原文

我们有一些文件存储在sql数据库中。在 ASP.NET MVC3 表单上,我们显示 2 个链接:

查看此文件 |下载此文件

这些链接可转到这些相应的操作方法。下载按预期进行——单击链接会强制在浏览器中显示保存对话框。但是,显示会导致重复的 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"

我尝试更改自定义内容处置标头以将文件名用双引号引起来,但它仍然向浏览器发送 2 个标头。我还尝试在添加自定义标头之前删除 Content-Disposition 标头,但似乎是在返回 FileContentResult 后添加附件标头。

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

更新

再次感谢达林,你是对的。我们实际上使用这种方法是因为您回答的另一个问题

关于我们最终如何解决这个问题的更多信息,我们有一个用于显示文件链接的自定义路由:

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

页面上的超链接通过路由参数将文件名传递给操作方法,因此它已经是网址。因此,当用户决定下载文件时(通过单击浏览器 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);
    }
}

We have some files stored in sql database. On an ASP.NET MVC3 form, we display 2 links:

View this file | Download this file

These links go to these corresponding action methods. The download works as expected -- clicking a link forces a save dialog in the browser. However, the display is causing duplicate Content-Disposition headers to be sent to the browser, resulting in an error on Chrome, and an empty page in 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);
    }
}

Here are the 2 headers sent to the browser for the display action:

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

I tried changing my custom content-disposition header to wrap the file name in double quotes, but it still sent 2 headers to the browser. I also tried removing the Content-Disposition header before adding the custom one, but it appears the attachment header is being added after the FileContentResult is returned.

This code used to work. I ran a test just yesterday and noticed it is no longer working in Chrome or Firefox. This could be due to updates in the browsers. IE8 and Safari still open the file correctly.

Update

Thanks again Darin, you are correct. We actually used this approach because of another question you answered.

A little more info about how this was ultimately solved on our end, we have a custom route for the display file link:

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

The hyperlink on the page passes the file name to the action method through the route parameter, so it is already part of the URL. Thus, we did not need to add a custom content-disposition header in order to make the file name match the system's when a user decided to download it (by clicking save icon in browser PDF viewer). So we just used this:

[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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冰魂雪魄 2024-12-29 18:47:37

当您使用重载 File(byte[]contents, string mimeType, string fileName) 时,Content-Disposition 标头会通过 attachment 自动添加到响应中code>,这样就不需要再次添加了。对于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);
}

When you use the overload File(byte[] contents, string mimeType, string fileName) a Content-Disposition header is automatically added to the response with attachment, so you don't need to add it a second time. For inline you could use the following overload File(byte[] contents, string mimeType) and manually add the Content-Disposition header:

[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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文