it-swarm-pt.tech

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

Eu estou encontrando um problema enviando arquivos armazenados em um banco de dados 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 deixar 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 visualizar um arquivo chamado SomeRandomFile.bak e o navegador não tiver um programa associado para abrir arquivos desse tipo, não tenho nenhum problema com o padrão do 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 eu também quero manter um link de download para o lado para que eu possa forçar um prompt de download, independentemente do tipo de arquivo. Isso faz sentido?

Eu tentei FileStreamResult e ele funciona para a maioria dos arquivos, o construtor não aceita um nome de arquivo por padrão, então arquivos desconhecidos são atribuídos a um nome de arquivo baseado no url (que não conhece 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?


PDATE: Esta questão parece ter um acorde com muitas pessoas, então eu pensei em postar uma atualização. O aviso sobre a resposta aceita abaixo que foi adicionado por Oskar em relação aos caracteres internacionais é completamente válido, e eu bati nele algumas vezes devido ao uso da classe ContentDisposition. Desde então, atualizei minha implementação para corrigir isso. Embora o código abaixo seja da minha versão mais recente desse problema em um aplicativo ASP.NET Core (Full Framework), ele deve funcionar com um mínimo de alterações em um aplicativo MVC mais antigo, já que estou usando a classe 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
}
271
Nick Albrecht
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 adequadamente para caracteres internacionais no nome do arquivo. Veja RFC6266 para a padronização relevante. Acredito que versões recentes do método File() do ASP.Net MVC e da classe ContentDispositionHeaderValue sejam responsáveis ​​por isso. - Oskar 2016-02-25

405
Darin Dimitrov

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

resposta de Darin Dimitrov está correto. 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 cabeçalho "Content-Disposition". Nesse caso, você pode querer usar:

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

Para ver arquivo (txt por exemplo):

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

Para download arquivo (txt por exemplo):

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

nota: para baixar o arquivo devemos passar fileDownloadName argumento

9
Ashot Muradian

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));
    }
3
Serj Sagan

ArquivoVirtualPath -> Research\Global Office Review.pdf

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

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<FileResult> PrintPdfStatements(string fileName)
    {
         var fileContent = await GetFileStreamAsync(fileName);
         var fileContentBytes = ((MemoryStream)fileContent).ToArray();
         return File(fileContentBytes, System.Net.Mime.MediaTypeNames.Application.Pdf);
    }
0
Jonny Boy