it-swarm.com.de

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

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

Die folgende Controller-Methode scheint zu funktionieren, dh, sie gibt eine Datei zurück, setzt den Inhaltstyp jedoch 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 einen besseren Weg, dies zu tun?

304
Josh Earl

Versuchen Sie es mit einem einfachen HttpResponseMessage , dessen Eigenschaft Content auf StreamContent festgelegt ist:

_// 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, die Sie über das verwendete stream beachten sollten:

  • Sie dürfen stream.Dispose() nicht aufrufen, da die Web-API immer noch darauf zugreifen muss, wenn sie die result-Methode der Steuerung verarbeitet, um Daten an den Client zurückzusenden. Verwenden Sie daher keinen using (var stream = …) -Block. Die Web-API wird den Stream für Sie bereitstellen.

  • Stellen Sie sicher, 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. Stellen Sie jedoch in anderen Szenarien (z. B. wenn Sie zum ersten Mal Binärdaten in ein MemoryStream schreiben) sicher, dass stream.Seek(0, SeekOrigin.Begin); oder set _stream.Position = 0;_

  • Bei Dateistreams kann die explizite Angabe der Berechtigung FileAccess.Read dazu beitragen, Probleme mit Zugriffsrechten auf Webservern zu vermeiden. IIS Anwendungspoolkonten erhalten häufig nur Lese-/Listen-/Ausführungszugriffsrechte für die WWW-Stammverzeichnisse.

494
carlosfigueira

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

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 so etwas in deinem 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, Anforderungen mit einer Erweiterung zu ignorieren, damit die Anforderung an den Controller gesendet wird:

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

Während die vorgeschlagene Lösung einwandfrei funktioniert, gibt es eine andere Möglichkeit, ein Byte-Array vom Controller zurückzugeben, wobei der Antwortdatenstrom ordnungsgemäß formatiert ist:

  • Setzen Sie in der Anfrage den Header "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 zu Ihrer globalen Konfiguration hinzufügen:

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

WebApi sollte jetzt BinaryMediaTypeFormatter verwenden, wenn die Anforderung den richtigen Accept-Header angibt.

Ich bevorzuge diese Lösung, da ein Action-Controller, der Byte [] zurückgibt, komfortabler zu testen ist. Mit der anderen Lösung können Sie jedoch besser steuern, ob Sie einen anderen Inhaltstyp als "application/octet-stream" zurückgeben möchten (z. B. "image/gif").

8
Eric Boumendil

Wenn Sie das Problem haben, dass die API beim Herunterladen einer relativ großen Datei mit der in der akzeptierten Antwort angegebenen Methode mehrmals aufgerufen wird, setzen Sie 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 Anforderungen an den Controller gesendet. Wenn Sie diese nicht ordnungsgemäß verarbeiten, wird die Datei beschädigt.

7
JBA

Die von Ihnen verwendete Überladung legt die Aufzählung der Serialisierungsformatierer fest. Sie müssen den Inhaltstyp explizit wie folgt angeben:

httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
6
David Peden

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 Punkt vermitteln. In .NET Core ist dieser Prozess , also viel einfacher als in früheren Versionen von .NET - d. H. Es werden keine Antworttypen, Inhalte, Header usw. festgelegt.

Natürlich hängt der MIME-Typ für die Datei und die Erweiterung auch von den individuellen Anforderungen ab.

Referenz: SO Antwort schreiben von @NKosi

5
KMJungersen

Du könntest es versuchen

httpResponseMessage.Content.Headers.Add("Content-Type", "application/octet-stream");
3
MickySmig