Как избежать дублирования заголовков размещения контента с MVC3 FileContentResult? - PullRequest
11 голосов
/ 23 декабря 2011

У нас есть несколько файлов, хранящихся в базе данных 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",
    }
);

Гиперссылка на странице передает имя файла методу действия через параметр маршрута, поэтому оно уже является частью URL. Таким образом, нам не нужно было добавлять пользовательский заголовок размещения контента, чтобы имя файла совпадало с именем системы, когда пользователь решил загрузить его (нажав на значок сохранения в браузере PDF Viewer). Итак, мы просто использовали это:

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

1 Ответ

27 голосов
/ 23 декабря 2011

При использовании перегрузки 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);
}
...