Devolver archivo en ASP.Net Core Web API

131

Problema

Quiero devolver un archivo en mi controlador de API web ASP.Net, pero todos mis enfoques devuelven HttpResponseMessagecomo JSON.

Código hasta ahora

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

Cuando llamo a este punto final en mi navegador, la API web devuelve HttpResponseMessagecomo JSON con el Encabezado de contenido HTTP establecido en application/json.

Jan Kruse
fuente

Respuestas:

228

Si se trata de ASP.net-Core, está mezclando versiones de API web. Haga que la acción devuelva un derivado IActionResultporque en su código actual el marco se trata HttpResponseMessagecomo un modelo.

[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
fuente
12
En mi caso, necesitaba representar un Excel en la memoria y devolverlo para descargar, así que también necesitaba definir un nombre de archivo con extensión: de return File(stream, "application/octet-stream", "filename.xlsx"); esta manera, cuando se descarga, el usuario puede abrirlo directamente.
KMJungersen
Entiendo lo que NotFound()finalmente hace, pero ¿reside en .NET Core o es algo local para su proyecto?
ΩmegaMan
2
@ ΩmegaMan es un método auxiliar ControllerBasey forma parte del marco en sí mismo docs.microsoft.com/en-us/dotnet/api/…
Nkosi
3
Ok, encontré mi problema, aunque mi controlador funcionaba en .NET Core 2.2, no se derivaba de la clase base y, Controllerpor lo tanto, no tenía acceso al ControllerBase.NotFound()método. Una vez derivado, todo funcionó. lol / thx
ΩmegaMan
1
¿Este método consume memoria del sistema en caso de que esté descargando archivos grandes del servidor? Mi primera suposición no es, dado el hecho de que no estamos creando un nuevo MemoryStream (). Agradecería una respuesta. thx
Ehsan Najafi
16

Puede devolver FileResult con estos métodos:

1: Devolver 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: Devolver 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
fuente
2
Si dentro de un ControllerBasehay muchas versiones sobrecargadas de ControllerBase.Fileayuda que devuelve cualquiera de ellas.
Nkosi
2
Tu respuesta sigue siendo válida. Así que no te sientas desanimado. Solo estaba señalando algunos recursos que puede usar para respaldar su respuesta.
Nkosi
1
Sí, es cierto.
Hamed Naeemaei
9

Aquí hay un ejemplo simplista de transmisión de un archivo:

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

Nota:

Asegúrese de usar FileStreamResultdesde Microsoft.AspNetCore.Mvcy no desde System.Web.Mvc.

gpresland
fuente