it-swarm.com.de

So geben Sie eine Datei (FileContentResult) in ASP.NET WebAPI zurück

In einem normalen MVC-Controller können wir PDF-Dateien mit einem FileContentResult ausgeben.

public FileContentResult Test(TestViewModel vm)
{
    var stream = new MemoryStream();
    //... add content to the stream.

    return File(stream.GetBuffer(), "application/pdf", "test.pdf");
}

Aber wie können wir es in ein ApiController verwandeln?

[HttpPost]
public IHttpActionResult Test(TestViewModel vm)
{
     //...
     return Ok(pdfOutput);
}

Folgendes habe ich versucht, aber es scheint nicht zu funktionieren.

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();
    //...
    var content = new StreamContent(stream);
    content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    content.Headers.ContentLength = stream.GetBuffer().Length;
    return Ok(content);            
}

Das im Browser angezeigte Ergebnis ist:

{"Headers":[{"Key":"Content-Type","Value":["application/pdf"]},{"Key":"Content-Length","Value":["152844"]}]}

Und es gibt einen ähnlichen Beitrag zu SO: Rückgabe der Binärdatei vom Controller in der ASP.NET-Web-API . Es geht um die Ausgabe einer vorhandenen Datei. Aber ich konnte es nicht mit einem Stream zum Laufen bringen.

Irgendwelche Vorschläge?

154
Blaise

Anstatt StreamContent als Content zurückzugeben, kann ich dafür sorgen, dass es mit ByteArrayContent funktioniert.

[HttpGet]
public HttpResponseMessage Generate()
{
    var stream = new MemoryStream();
    // processing the stream.

    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.ToArray())
    };
    result.Content.Headers.ContentDisposition =
        new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "CertificationCard.pdf"
    };
    result.Content.Headers.ContentType =
        new MediaTypeHeaderValue("application/octet-stream");

    return result;
}
176
Blaise

Wenn Sie IHttpActionResult zurückgeben möchten, können Sie dies folgendermaßen tun:

[HttpGet]
public IHttpActionResult Test()
{
    var stream = new MemoryStream();

    var result = new HttpResponseMessage(HttpStatusCode.OK)
    {
        Content = new ByteArrayContent(stream.GetBuffer())
    };
    result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
    {
        FileName = "test.pdf"
    };
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

    var response = ResponseMessage(result);

    return response;
}
81
Ogglas

Diese Frage hat mir geholfen.

Also probieren Sie Folgendes:

Controller-Code:

[HttpGet]
public HttpResponseMessage Test()
{
    var path = System.Web.HttpContext.Current.Server.MapPath("~/Content/test.docx");;
    HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
    var stream = new FileStream(path, FileMode.Open);
    result.Content = new StreamContent(stream);
    result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
    result.Content.Headers.ContentDisposition.FileName = Path.GetFileName(path);
    result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    result.Content.Headers.ContentLength = stream.Length;
    return result;          
}

HTML-Markup anzeigen (mit Klickereignis und einfacher URL):

<script type="text/javascript">
    $(document).ready(function () {
        $("#btn").click(function () {
            // httproute = "" - using this to construct proper web api links.
            window.location.href = "@Url.Action("GetFile", "Data", new { httproute = "" })";
        });
    });
</script>


<button id="btn">
    Button text
</button>

<a href=" @Url.Action("GetFile", "Data", new { httproute = "" }) ">Data</a>
40
aleha

Ich bin mir nicht sicher, welcher Teil schuld ist, aber hier ist der Grund, warum MemoryStream bei Ihnen nicht funktioniert:

Wenn Sie in MemoryStream schreiben, wird die Eigenschaft Position erhöht. Der Konstruktor von StreamContent berücksichtigt den aktuellen Position des Streams. Wenn Sie also in den Stream schreiben und ihn an StreamContent übergeben, beginnt die Antwort mit dem Nichts am Ende des Streams.

Es gibt zwei Möglichkeiten, dies zu beheben:

1) konstruiere Inhalte, schreibe in den Stream

[HttpGet]
public HttpResponseMessage Test()
{
    var stream = new MemoryStream();
    var response = Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StreamContent(stream);
    // ...
    // stream.Write(...);
    // ...
    return response;
}

2) In Stream schreiben, Position zurücksetzen, Inhalt erstellen

[HttpGet]
public HttpResponseMessage Test()
{
    var stream = new MemoryStream();
    // ...
    // stream.Write(...);
    // ...
    stream.Position = 0;

    var response = Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StreamContent(stream);
    return response;
}

2) Sieht ein bisschen besser aus, wenn Sie einen neuen Stream haben, 1) ist einfacher, wenn Ihr Stream nicht bei 0 beginnt

6
M.Stramm

Hier ist eine Implementierung, die den Inhalt der Datei streamt, ohne ihn zu puffern (das Puffern in Byte []/MemoryStream usw. kann ein Serverproblem sein, wenn es sich um eine große Datei handelt).

public class FileResult : IHttpActionResult
{
    public FileResult(string filePath)
    {
        if (filePath == null)
            throw new ArgumentNullException(nameof(filePath));

        FilePath = filePath;
    }

    public string FilePath { get; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK);
        response.Content = new StreamContent(File.OpenRead(FilePath));
        var contentType = MimeMapping.GetMimeMapping(Path.GetExtension(FilePath));
        response.Content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
        return Task.FromResult(response);
    }
}

Es kann einfach so verwendet werden:

public class MyController : ApiController
{
    public IHttpActionResult Get()
    {
        string filePath = GetSomeValidFilePath();
        return new FileResult(filePath);
    }
}
4
Simon Mourier

Für mich war es der Unterschied zwischen

var response = Request.CreateResponse(HttpStatusCode.OK, new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

und

var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(log, System.Text.Encoding.UTF8, "application/octet-stream");

Der erste gab die JSON-Darstellung von StringContent zurück: {"Headers": [{"Key": "Content-Type", "Value": ["application/octet-stream; charset = utf-8"]}}

Während der zweite die eigentliche Datei zurückgab.

Es scheint, dass Request.CreateResponse eine Überladung hat, die eine Zeichenfolge als zweiten Parameter verwendet. Dies hat anscheinend dazu geführt, dass das StringContent-Objekt selbst als Zeichenfolge statt als tatsächlicher Inhalt gerendert wurde.

3
EnderWiggin