web-dev-qa-db-fra.com

Retour d'un fichier à View / Download dans ASP.NET MVC

Je rencontre un problème lors de l'envoi des fichiers stockés dans une base de données à l'utilisateur dans ASP.NET MVC. Ce que je veux, c'est une vue répertoriant deux liens, l'un pour afficher le fichier et laisser le type MIME envoyé au navigateur déterminer comment il doit être géré, et l'autre pour forcer le téléchargement.

Si je choisis d'afficher un fichier nommé SomeRandomFile.bak et que le navigateur ne dispose pas d'un programme associé pour ouvrir des fichiers de ce type, le comportement par défaut du téléchargement ne me pose aucun problème. Cependant, si je choisis d'afficher un fichier appelé SomeRandomFile.pdf ou SomeRandomFile.jpg, je veux que le fichier s'ouvre simplement. Mais je veux aussi garder un lien de téléchargement sur le côté afin de pouvoir forcer une invite de téléchargement quel que soit le type de fichier. Est-ce que ça a du sens?

J'ai essayé FileStreamResult et cela fonctionne pour la plupart des fichiers, son constructeur n'accepte pas un nom de fichier par défaut, donc les fichiers inconnus se voient attribuer un nom de fichier basé sur l'URL (qui ne connaît pas l'extension à donner en fonction type de contenu). Si je force le nom de fichier en le spécifiant, le navigateur ne peut plus ouvrir le fichier directement et je reçois une invite de téléchargement. Quelqu'un d'autre a-t-il rencontré ce problème?.

Ce sont les exemples de ce que j'ai essayé jusqu'à présent.

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

Aucune suggestion?


PDATE: Cette question semble toucher beaucoup de monde, alors j'ai pensé poster une mise à jour. L’avertissement sur la réponse acceptée ci-dessous qui a été ajouté par Oskar concernant les caractères internationaux est tout à fait valide. Je l’ai déjà frappé plusieurs fois en raison de l’utilisation de la classe ContentDisposition. J'ai depuis mis à jour mon implémentation pour résoudre ce problème. Bien que le code ci-dessous provienne de ma plus récente incarnation de ce problème dans une application ASP.NET Core (Full Framework), il devrait fonctionner avec des modifications minimes dans une ancienne application MVC, car j'utilise la 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
}
277
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);
}

REMARQUE: Cet exemple de code ci-dessus ne permet pas de prendre en compte correctement les caractères internationaux dans le nom de fichier. Voir RFC6266 pour la standardisation correspondante. Je crois que les versions récentes de la méthode File() d'ASP.Net MVC et de la classe ContentDispositionHeaderValue rendent bien compte de cela. - Oskar 2016-02-25

409
Darin Dimitrov

J'ai eu des problèmes avec la réponse acceptée en raison de l'absence d'indication de type sur la variable "document": var document = ... Je publie donc ce qui a fonctionné pour moi comme solution de rechange au cas où quelqu'un aurait des problèmes.

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

La réponse de Darin Dimitrov est correcte. Juste un ajout:

Response.AppendHeader("Content-Disposition", cd.ToString()); peut empêcher le navigateur de restituer le fichier si votre réponse contient déjà un en-tête "Content-Disposition". Dans ce cas, vous voudrez peut-être utiliser:

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

Pour voir fichier (txt par exemple):

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

Pour télécharger fichier (txt par exemple):

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

remarque: pour télécharger le fichier, nous devons passer fileDownloadName argument

9
Ashot Muradian

Je crois que cette réponse est plus propre, (basé sur 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

FileVirtualPath -> Research\Global Office Review.pdf

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

Le code ci-dessous a fonctionné pour moi pour obtenir un fichier PDF à partir d’un service API et le répondre au navigateur - espérons que cela aide;

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