348 Stimmen

Rückgabe einer Binärdatei vom Controller in ASP.NET Web API

Ich arbeite an einem Webdienst, der die neue WebAPI von ASP.NET MVC verwendet und Binärdateien bereitstellt, hauptsächlich .cab y .exe Dateien.

Die folgende Controller-Methode scheint zu funktionieren, was bedeutet, dass sie eine Datei zurückgibt, aber sie setzt den Inhaltstyp auf application/json :

public HttpResponseMessage<Stream> Post(string version, string environment, string filetype)
{
    var path = @"C:\Temp\test.exe";
    var stream = new FileStream(path, FileMode.Open);
    return new HttpResponseMessage<Stream>(stream, new MediaTypeHeaderValue("application/octet-stream"));
}

Gibt es eine bessere Möglichkeit, dies zu tun?

543voto

carlosfigueira Punkte 82509

Versuchen Sie es mit einem einfachen HttpResponseMessage mit seinem Content Eigenschaft, die auf eine StreamContent :

// using System.IO;
// using System.Net.Http;
// using System.Net.Http.Headers;

public HttpResponseMessage Post(string version, string environment,
    string filetype)
{
    var path = @"C:\Temp\test.exe";
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentType = 
        new MediaTypeHeaderValue("application/octet-stream");
    return result;
}

Ein paar Dinge zum Thema stream verwendet:

  • Sie dürfen nicht anrufen stream.Dispose() da die Web-API immer noch in der Lage sein muss, darauf zuzugreifen, wenn sie die Controller-Methode result um Daten an den Client zurückzusenden. Verwenden Sie daher keine using (var stream = …) Block. Die Web-API wird den Stream für Sie entsorgen.

  • Vergewissern Sie sich, dass die aktuelle Position des Streams auf 0 gesetzt ist (d. h. der Beginn der Daten des Streams). Im obigen Beispiel ist dies eine Selbstverständlichkeit, da Sie die Datei gerade erst geöffnet haben. In anderen Fällen (z. B. wenn Sie zum ersten Mal binäre Daten in eine MemoryStream ), stellen Sie sicher, dass stream.Seek(0, SeekOrigin.Begin); oder setzen stream.Position = 0;

  • Bei Dateistreams ist die explizite Angabe von FileAccess.Read Berechtigung kann dazu beitragen, Probleme mit den Zugriffsrechten auf Webservern zu vermeiden; IIS-Anwendungspoolkonten erhalten oft nur Lese-/Listen-/Ausführungsrechte für das wwwroot.

146voto

Ronnie Overby Punkte 43323

Für Web API 2 können Sie implementieren IHttpActionResult . Hier ist meine:

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;

class FileResult : IHttpActionResult
{
    private readonly string _filePath;
    private readonly string _contentType;

    public FileResult(string filePath, string contentType = null)
    {
        if (filePath == null) throw new ArgumentNullException("filePath");

        _filePath = filePath;
        _contentType = contentType;
    }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StreamContent(File.OpenRead(_filePath))
        };

        var contentType = _contentType ?? MimeMapping.GetMimeMapping(Path.GetExtension(_filePath));
        response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);

        return Task.FromResult(response);
    }
}

Dann etwas wie dieses in Ihrem Controller:

[Route("Images/{*imagePath}")]
public IHttpActionResult GetImage(string imagePath)
{
    var serverPath = Path.Combine(_rootPath, imagePath);
    var fileInfo = new FileInfo(serverPath);

    return !fileInfo.Exists
        ? (IHttpActionResult) NotFound()
        : new FileResult(fileInfo.FullName);
}

Und hier ist eine Möglichkeit, wie Sie IIS anweisen können, Anfragen mit einer Erweiterung zu ignorieren, damit die Anfrage an den Controller weitergeleitet wird:

<!-- web.config -->
<system.webServer>
  <modules runAllManagedModulesForAllRequests="true"/>

37voto

Kurtis Jungersen Punkte 1974

Für diejenigen, die .NET Core verwenden:

Sie können die IActionResult-Schnittstelle in einer API-Controller-Methode wie folgt verwenden.

[HttpGet("GetReportData/{year}")]
public async Task<IActionResult> GetReportData(int year)
{
    // Render Excel document in memory and return as Byte[]
    Byte[] file = await this._reportDao.RenderReportAsExcel(year);

    return File(file, "application/vnd.openxmlformats", "fileName.xlsx");
}

Dieses Beispiel ist vereinfacht, sollte aber den Sinn der Sache verdeutlichen. In .NET Core ist dieser Prozess also viel einfacher als in früheren Versionen von .NET - d.h. keine Einstellung von Antworttyp, Inhalt, Header usw.

Auch der MIME-Typ für die Datei und die Erweiterung hängen natürlich von den individuellen Bedürfnissen ab.

Referenz: SO Post Antwort von @NKosi

10voto

Eric Boumendil Punkte 2228

Während die vorgeschlagene Lösung funktioniert gut, gibt es eine andere Möglichkeit, ein Byte-Array aus dem Controller zurück, mit Antwort-Stream richtig formatiert:

  • Setzen Sie in der Anfrage die Kopfzeile "Accept: application/octet-stream".
  • Fügen Sie serverseitig einen Medientyp-Formatierer hinzu, um diesen Mime-Typ zu unterstützen.

Leider enthält WebApi keinen Formatierer für "application/octet-stream". Es gibt eine Implementierung hier auf GitHub: BinaryMediaTypeFormatter (es gibt kleinere Anpassungen, damit es für webapi 2 funktioniert, Methodensignaturen geändert).

Sie können diesen Formatierer in Ihre globale Konfiguration aufnehmen:

HttpConfiguration config;
// ...
config.Formatters.Add(new BinaryMediaTypeFormatter(false));

WebApi sollte nun BinaryMediaTypeFormatter wenn die Anfrage den richtigen Accept-Header angibt.

Ich bevorzuge diese Lösung, weil ein Action Controller, der Byte[] zurückgibt, bequemer zu testen ist. Die andere Lösung erlaubt Ihnen jedoch mehr Kontrolle, wenn Sie einen anderen Inhaltstyp als "application/octet-stream" zurückgeben möchten (zum Beispiel "image/gif").

9voto

JBA Punkte 220

Für alle, die das Problem haben, dass die API mehr als einmal aufgerufen wird, während sie eine ziemlich große Datei mit der Methode in der akzeptierten Antwort herunterladen, setzen Sie bitte die Antwortpufferung auf true System.Web.HttpContext.Current.Response.Buffer = true;

Dadurch wird sichergestellt, dass der gesamte binäre Inhalt auf der Serverseite gepuffert wird, bevor er an den Client gesendet wird. Andernfalls werden mehrere Anfragen an die Steuereinheit gesendet, und wenn Sie dies nicht richtig handhaben, wird die Datei beschädigt.

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