340 Stimmen

Rückgabe einer Datei zur Ansicht/Download in ASP.NET MVC

Ich stoße auf ein Problem, das Senden von Dateien in einer Datenbank gespeichert zurück an den Benutzer in ASP.NET MVC. Was ich will, ist eine Ansicht, die zwei Links auflistet, eine, um die Datei anzuzeigen und lassen Sie den Mimetyp an den Browser gesendet bestimmen, wie es behandelt werden soll, und die andere, um einen Download zu erzwingen.

Wenn ich eine Datei namens SomeRandomFile.bak und der Browser kein zugehöriges Programm zum Öffnen von Dateien dieses Typs hat, dann habe ich kein Problem damit, dass er standardmäßig das Download-Verhalten anwendet. Wenn ich jedoch eine Datei namens SomeRandomFile.pdf ou SomeRandomFile.jpg Ich möchte, dass die Datei einfach geöffnet wird. Aber ich möchte auch einen Download-Link auf der Seite, so dass ich eine Download-Aufforderung unabhängig vom Dateityp erzwingen kann. Ist dies sinnvoll?

Ich habe versucht FileStreamResult und es funktioniert für die meisten Dateien, sein Konstruktor akzeptiert standardmäßig keinen Dateinamen, so dass unbekannte Dateien einen Dateinamen auf der Grundlage der URL zugewiesen werden (die nicht weiß, welche Erweiterung auf der Grundlage des Inhaltstyps zu geben). Wenn ich den Dateinamen erzwinge, indem ich ihn angebe, kann der Browser die Datei nicht mehr direkt öffnen und ich erhalte eine Aufforderung zum Herunterladen. Hat jemand dieses Problem schon einmal gehabt?

Dies sind die Beispiele, die ich bisher ausprobiert habe.

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

Irgendwelche Vorschläge?


UPDATE: Diese Frage scheint viele Leute zu beschäftigen, also dachte ich, ich bringe sie mal auf den neuesten Stand. Die Warnung in der akzeptierten Antwort unten, die von Oskar in Bezug auf internationale Zeichen hinzugefügt wurde, ist absolut gültig, und ich habe sie ein paar Mal getroffen, weil ich die ContentDisposition Klasse. Ich habe seitdem meine Implementierung aktualisiert, um dies zu beheben. Während der Code unten von meiner neuesten Inkarnation dieses Problems in einer ASP.NET Core (Full Framework)-App ist, sollte es mit minimalen Änderungen in einer älteren MVC-Anwendung auch funktionieren, da ich die System.Net.Http.Headers.ContentDispositionHeaderValue Klasse.

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
}

4voto

Jonny Boy Punkte 180

Der folgende Code funktionierte bei mir, um eine PDF-Datei von einem API-Dienst abzurufen und sie an den Browser weiterzuleiten - ich hoffe, er hilft;

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

3voto

VETRIVEL D Punkte 81

Die Action-Methode muss FileResult entweder mit einem Stream, einem Byte[] oder einem virtuellen Pfad der Datei zurückgeben. Sie müssen auch den Inhaltstyp der heruntergeladenen Datei kennen. Hier ist ein Beispiel für eine (schnelle/schmutzige) Utility-Methode. Beispiel für einen Videolink Wie man Dateien mit asp.net core herunterlädt

[Route("api/[controller]")]
public class DownloadController : Controller
{
    [HttpGet]
    public async Task<IActionResult> Download()
    {
        var path = @"C:\Vetrivel\winforms.png";
        var memory = new MemoryStream();
        using (var stream = new FileStream(path, FileMode.Open))
        {
            await stream.CopyToAsync(memory);
        }
        memory.Position = 0;
        var ext = Path.GetExtension(path).ToLowerInvariant();
        return File(memory, GetMimeTypes()[ext], Path.GetFileName(path));
    }

    private Dictionary<string, string> GetMimeTypes()
    {
        return new Dictionary<string, string>
        {
            {".txt", "text/plain"},
            {".pdf", "application/pdf"},
            {".doc", "application/vnd.ms-word"},
            {".docx", "application/vnd.ms-word"},
            {".png", "image/png"},
            {".jpg", "image/jpeg"},
            ...
        };
    }
}

2voto

VorTechS Punkte 413

Wenn Sie, wie ich, beim Erlernen von Blazor über Razor-Komponenten zu diesem Thema gekommen sind, dann werden Sie feststellen, dass Sie ein wenig mehr über den Tellerrand schauen müssen, um dieses Problem zu lösen. Es ist ein bisschen wie ein Minenfeld, wenn (auch wie bei mir) Blazor Ihr erster Ausflug in die MVC-Welt ist, da die Dokumentation nicht so hilfreich für solche "niederen" Aufgaben ist.

Zum Zeitpunkt des Schreibens können Sie dies also nicht mit Vanilla Blazor/Razor erreichen, ohne einen MVC-Controller einzubetten, um den Teil des Dateidownloads zu handhaben - ein Beispiel dafür finden Sie unten:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;

[Route("api/[controller]")]
[ApiController]
public class FileHandlingController : ControllerBase
{
    [HttpGet]
    public FileContentResult Download(int attachmentId)
    {
        TaskAttachment taskFile = null;

        if (attachmentId > 0)
        {
            // taskFile = <your code to get the file>
            // which assumes it's an object with relevant properties as required below

            if (taskFile != null)
            {
                var cd = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
                {
                    FileNameStar = taskFile.Filename
                };

                Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
            }
        }

        return new FileContentResult(taskFile?.FileData, taskFile?.FileContentType);
    }
}

Stellen Sie als Nächstes sicher, dass Ihr Anwendungsstart (Startup.cs) für die korrekte Verwendung von MVC konfiguriert ist und die folgende Zeile enthält (falls nicht, fügen Sie sie hinzu):

        services.AddMvc();

und ändern Sie dann schließlich Ihre Komponente, um sie mit dem Controller zu verknüpfen (iteratives Beispiel mit einer benutzerdefinierten Klasse):

    <tbody>
        @foreach (var attachment in yourAttachments)
        {
        <tr>
            <td><a href="api/FileHandling?attachmentId=@attachment.TaskAttachmentId" target="_blank">@attachment.Filename</a> </td>
            <td>@attachment.CreatedUser</td>
            <td>@attachment.Created?.ToString("dd MMM yyyy")</td>
            <td><ul><li class="oi oi-circle-x delete-attachment"></li></ul></td>
        </tr>
        }
        </tbody>

Ich hoffe, dies hilft allen, die (wie ich!) Schwierigkeiten hatten, eine angemessene Antwort auf diese scheinbar einfache Frage in den Gefilden von Blazor zu finden !

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X