Retornando um arquivo para View / Download no asp.net MVC

Eu estou encontrando um problema enviando arquivos armazenados em um database de volta para o usuário no asp.net MVC. O que eu quero é uma visualização listando dois links, um para exibir o arquivo e permitir que o tipo MIME enviado ao navegador determine como ele deve ser manipulado e o outro para forçar um download.

Se eu optar por ver um arquivo chamado SomeRandomFile.bak e o navegador não tiver um programa associado para abrir arquivos desse tipo, não tenho nenhum problema em padronizar o comportamento de download. No entanto, se eu optar por ver um arquivo chamado SomeRandomFile.pdf ou SomeRandomFile.jpg , quero que o arquivo seja aberto simplesmente. Mas também quero manter um link de download ao lado para que eu possa forçar um prompt de download, independentemente do tipo de arquivo. Isso faz sentido?

Eu tentei FileStreamResult e funciona para a maioria dos arquivos, é construtor não aceita um nome de arquivo por padrão, então arquivos desconhecidos são atribuídos a um nome de arquivo com base na URL (que não sabe a extensão para dar com base no tipo de conteúdo). Se eu forçar o nome do arquivo especificando-o, perco a capacidade do navegador de abrir o arquivo diretamente e recebo um prompt de download. Mais alguém encontrou isso.

Estes são os exemplos do que eu tentei até agora.

 //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}; 

Alguma sugestão?

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

Observação: este código de exemplo acima não conta corretamente para caracteres internacionais no nome do arquivo. Veja RFC6266 para a padronização relevante. Eu acredito que versões recentes do método File() do asp.net MVC e a class ContentDispositionHeaderValue corretamente contas para isso. – Oskar 25/02/2016

Eu tive problemas com a resposta aceita devido a nenhum tipo de insinuação sobre a variável “document”: var document = ... Então estou postando o que funcionou para mim como uma alternativa no caso de alguém estar com problemas.

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

Para ver o arquivo (txt por exemplo):

 return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain); 

Para baixar o arquivo (txt por exemplo):

 return File("~/TextFileInRootDir.txt", MediaTypeNames.Text.Plain, "TextFile.txt"); 

Nota: para baixar o arquivo, devemos passar o argumento fileDownloadName

A resposta de Darin Dimitrov está correta. Apenas uma adição:

Response.AppendHeader("Content-Disposition", cd.ToString()); pode fazer com que o navegador falhe ao processar o arquivo se sua resposta já contiver um header “Content-Disposition”. Nesse caso, você pode querer usar:

 Response.Headers.Add("Content-Disposition", cd.ToString()); 

ArquivoVirtualPath -> Research \ Global Office Review.pdf

 public virtual ActionResult GetFile() { return File(FileVirtualPath, "application/force-download", Path.GetFileName(FileVirtualPath)); } 

Eu acredito que esta resposta é mais limpa, (com base em https://stackoverflow.com/a/3007668/550975 )

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

Abaixo o código funcionou para mim para obter um arquivo pdf de um serviço de API e respondê-lo para o navegador – espero que ajude;

 public async Task PrintPdfStatements(string fileName) { var fileContent = await GetFileStreamAsync(fileName); var fileContentBytes = ((MemoryStream)fileContent).ToArray(); return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf); } 

Outra maneira fácil é

 public ActionResult GetLoadingLogo() { if (1 == 1) { return new FilePathResult(Server.MapPath("~/Content/Images/ajaxLoader_dhanlaxmi.gif"), "image/gif"); } else { return new FilePathResult(Server.MapPath("~/Content/Images/add-icon.png"), "image/png"); } }