Возврат файла для просмотра/загрузки в ASP.NET MVC
Я столкнулся с проблемой отправки файлов, хранящихся в базе данных, обратно пользователю в ASP.NET MVC. То, что я хочу, - это представление, в котором перечислены две ссылки: одна для просмотра файла, и пусть mimetype, отправляемый в браузер, определяет способ его обработки, а другая для принудительной загрузки.
Если я SomeRandomFile.bak
просмотреть файл с именем SomeRandomFile.bak
и у браузера не будет связанной программы для открытия файлов этого типа, то у меня не SomeRandomFile.bak
проблем с его настройками по умолчанию при загрузке. Однако, если я SomeRandomFile.pdf
просмотреть файл с именем SomeRandomFile.pdf
или SomeRandomFile.jpg
я хочу, чтобы файл просто открылся. Но я также хочу оставить ссылку для скачивания в стороне, чтобы можно было принудительно вызвать приглашение к загрузке независимо от типа файла. Имеет ли это смысл?
Я пробовал FileStreamResult
и он работает для большинства файлов, по умолчанию конструктор не принимает имя файла, поэтому неизвестным файлам присваивается имя файла на основе URL-адреса (который не знает расширения для предоставления на основе типа содержимого). Если я задаю имя файла, указав его, я теряю возможность браузера открывать файл напрямую и получаю приглашение на загрузку. Кто-нибудь еще сталкивался с этим.
Это примеры того, что я пробовал до сих пор.
//Gives me a download prompt.
return File(document.Data, document.ContentType, document.Name);
//Opens if it is a known extension type, downloads otherwise (download has bogus name and missing extension)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType);
//Gives me a download prompt (lose the ability to open by default if known type)
return new FileStreamResult(new MemoryStream(document.Data), document.ContentType) {FileDownloadName = document.Name};
Какие-либо предложения?
ОБНОВЛЕНИЕ: Эти вопросы, кажется, вызывают отклик у многих людей, поэтому я решил опубликовать обновление. Предупреждение о принятом ответе ниже, которое было добавлено Оскаром в отношении международных символов, полностью допустимо, и я несколько раз ударил его из-за использования класса ContentDisposition
. С тех пор я обновил свою реализацию, чтобы исправить это. Хотя приведенный ниже код взят из моего последнего воплощения этой проблемы в приложении ASP.NET Core (Full Framework), он должен работать с минимальными изменениями и в старом приложении MVC, так как я использую System.Net.Http.Headers.ContentDispositionHeaderValue
Класс System.Net.Http.Headers.ContentDispositionHeaderValue
.
using System.Net.Http.Headers;
public IActionResult Download()
{
Document document = ... //Obtain document from database context
//"attachment" means always prompt the user to download
//"inline" means let the browser try and handle it
var cd = new ContentDispositionHeaderValue("attachment")
{
FileNameStar = document.FileName
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
return File(document.Data, document.ContentType);
}
// an entity class for the document in my database
public class Document
{
public string FileName { get; set; }
public string ContentType { get; set; }
public byte[] Data { get; set; }
//Other properties left out for brevity
}
Ответы
Ответ 1
public ActionResult Download()
{
var document = ...
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = document.FileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
ПРИМЕЧАНИЕ. В приведенном выше примере кода не удается правильно зарегистрировать международные символы в имени файла. См. RFC6266 для соответствующей стандартизации. Я считаю, что последние версии метода ASP.Net MVC File()
и класс ContentDispositionHeaderValue
должным образом учитывают это. - Oskar 2016-02-25
Ответ 2
У меня возникли проблемы с принятым ответом из-за отсутствия намека на переменную "document": var document = ...
Итак, я отправляю то, что сработало для меня как альтернатива, если у кого-то еще есть проблемы.
public ActionResult DownloadFile()
{
string filename = "File.pdf";
string filepath = AppDomain.CurrentDomain.BaseDirectory + "/Path/To/File/" + filename;
byte[] filedata = System.IO.File.ReadAllBytes(filepath);
string contentType = MimeMapping.GetMimeMapping(filepath);
var cd = new System.Net.Mime.ContentDisposition
{
FileName = filename,
Inline = true,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(filedata, contentType);
}
Ответ 3
Дарин Димитров отвечает. Просто добавление:
Response.AppendHeader("Content-Disposition", cd.ToString());
может привести к тому, что браузер не сможет выполнить рендеринг файла, если ваш ответ уже содержит заголовок "Content-Disposition". В этом случае вы можете использовать:
Response.Headers.Add("Content-Disposition", cd.ToString());
Ответ 4
Чтобы просмотреть файл (например, txt):
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain);
Чтобы загрузить файл (например, txt):
return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt");
note: для загрузки файла мы должны передать аргумент fileDownloadName
Ответ 5
Я считаю, что этот ответ чище (на основе
fooobar.com/questions/27999/...)
public ActionResult GetAttachment(long id)
{
FileAttachment attachment;
using (var db = new TheContext())
{
attachment = db.FileAttachments.FirstOrDefault(x => x.Id == id);
}
return File(attachment.FileData, "application/force-download", Path.GetFileName(attachment.FileName));
}
Ответ 6
FileVirtualPath → Исследование \Global Office Review.pdf
public virtual ActionResult GetFile()
{
return File(FileVirtualPath, "application/force-download", Path.GetFileName(FileVirtualPath));
}
Ответ 7
Приведенный ниже код помог мне получить pdf файл из службы API и отправить его в браузер - надеюсь, это поможет;
public async Task<FileResult> PrintPdfStatements(string fileName)
{
var fileContent = await GetFileStreamAsync(fileName);
var fileContentBytes = ((MemoryStream)fileContent).ToArray();
return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
}