Zwróć plik w ASP.Net Core Web API

131

Problem

Chcę zwrócić plik w moim kontrolerze interfejsu API sieci Web ASP.Net, ale wszystkie moje podejścia zwracają plik HttpResponseMessage jako JSON.

Kod do tej pory

public async Task<HttpResponseMessage> DownloadAsync(string id)
{
    var response = new HttpResponseMessage(HttpStatusCode.OK);
    response.Content = new StreamContent({{__insert_stream_here__}});
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
    return response;
}

Kiedy wywołuję ten punkt końcowy w mojej przeglądarce, interfejs API sieci Web zwraca HttpResponseMessageas JSON z nagłówkiem zawartości HTTP ustawionym na application/json.

Jan Kruse
źródło

Odpowiedzi:

228

Jeśli jest to ASP.net-Core, mieszasz wersje internetowego interfejsu API. Niech akcja zwraca pochodną, IActionResultponieważ w bieżącym kodzie framework traktuje HttpResponseMessagejako model.

[Route("api/[controller]")]
public class DownloadController : Controller {
    //GET api/download/12345abc
    [HttpGet("{id}"]
    public async Task<IActionResult> Download(string id) {
        Stream stream = await {{__get_stream_based_on_id_here__}}

        if(stream == null)
            return NotFound(); // returns a NotFoundResult with Status404NotFound response.

        return File(stream, "application/octet-stream"); // returns a FileStreamResult
    }    
}
Nkosi
źródło
12
W moim przypadku musiałem wyrenderować Excel w pamięci i zwrócić go do pobrania, więc musiałem zdefiniować również nazwę pliku z rozszerzeniem: w return File(stream, "application/octet-stream", "filename.xlsx"); ten sposób, gdy pobiera, użytkownik może go bezpośrednio otworzyć.
KMJungersen
Rozumiem, co NotFound()ostatecznie robi, ale czy znajduje się w .NET Core, czy jest to coś lokalnego dla twojego projektu?
ΩmegaMan
2
@ ΩmegaMan jest to metoda pomocnicza ControllerBasei jest częścią samej struktury docs.microsoft.com/en-us/dotnet/api/…
Nkosi
3
Ok, znalazłem mój problem, chociaż mój kontroler działał w .NET Core 2.2, nie został wyprowadzony z klasy bazowej, Controllerprzez co nie miał dostępu do ControllerBase.NotFound()metody. Po uzyskaniu wszystko działało. lol / thx
ΩmegaMan
1
Czy ta metoda zużywa pamięć systemową w przypadku pobierania dużych plików z serwera? Moje pierwsze przypuszczenie to nie, biorąc pod uwagę fakt, że nie tworzymy nowego MemoryStream (). Byłbym wdzięczny za odpowiedź. thx
Ehsan Najafi
16

Możesz zwrócić FileResult za pomocą tych metod:

1: Zwróć FileStreamResult

    [HttpGet("get-file-stream/{id}"]
    public async Task<FileStreamResult> DownloadAsync(string id)
    {
        var fileName="myfileName.txt";
        var mimeType="application/...."; 
        var stream = await GetFileStreamById(id);

        return new FileStreamResult(stream, mimeType)
        {
            FileDownloadName = fileName
        };
    }

2: Zwróć FileContentResult

    [HttpGet("get-file-content/{id}"]
    public async Task<FileContentResult> DownloadAsync(string id)
    {
        var fileName="myfileName.txt";
        var mimeType="application/...."; 
        var fileBytes = await GetFileBytesById(id);

        return new FileContentResult(fileBytes, mimeType)
        {
            FileDownloadName = fileName
        };
    }
Hamed Naeemaei
źródło
2
Jeśli w obrębie ControllerBaseznajduje się wiele przeciążonych wersji ControllerBase.Filepomocnika, który zwraca jeden z nich.
Nkosi
2
Twoja odpowiedź jest nadal aktualna. Więc nie czuj się zniechęcony. Właśnie wskazałem kilka zasobów, z których możesz skorzystać, aby wesprzeć swoją odpowiedź.
Nkosi
1
Tak to prawda.
Hamed Naeemaei
10

Oto uproszczony przykład przesyłania strumieniowego pliku:

using System.IO;
using Microsoft.AspNetCore.Mvc;
[HttpGet("{id}")]
public async Task<FileStreamResult> Download(int id)
{
    var path = "<Get the file path using the ID>";
    var stream = File.OpenRead(path);
    return new FileStreamResult(stream, "application/octet-stream");
}

Uwaga:

Pamiętaj, aby używać FileStreamResultfrom, Microsoft.AspNetCore.Mvca nie from System.Web.Mvc.

gpresland
źródło