Retornar arquivo na API da Web do ASP.Net Core

131

Problema

Desejo retornar um arquivo no meu controlador de API da Web do ASP.Net, mas todas as minhas abordagens retornam HttpResponseMessagecomo JSON.

Código até agora

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

Quando chamo esse terminal no meu navegador, a API da Web retorna HttpResponseMessagecomo JSON com o cabeçalho de conteúdo HTTP definido como application/json.

Jan Kruse
fonte

Respostas:

228

Se esse for o ASP.net-Core, você estará misturando versões da API da web. Faça com que a ação retorne uma derivada, IActionResultporque no seu código atual a estrutura está tratando HttpResponseMessagecomo um 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
fonte
12
No meu caso, eu precisava renderizar um Excel na memória e devolvê-lo para download. Por isso, também era necessário definir um nome de arquivo com extensão: return File(stream, "application/octet-stream", "filename.xlsx"); Dessa forma, ao fazer o download, o usuário pode abri-lo diretamente.
precisa saber é o seguinte
Entendo o que NotFound()isso faz, mas ele reside no .NET Core ou é algo local no seu projeto?
ΩmegaMan
2
@ EgamegaMan, é um método auxiliar ControllerBasee faz parte da própria estrutura docs.microsoft.com/en-us/dotnet/api/…
Nkosi
3
Ok, encontrei meu problema, embora meu controlador estivesse trabalhando no .NET Core 2.2, ele não era derivado da classe base Controllere, portanto, não tinha acesso ao ControllerBase.NotFound()método. Uma vez derivado, tudo funcionou. lol / thx
ΩmegaMan
1
Este método consome memória do sistema no caso de você estar baixando arquivos grandes do servidor? Meu primeiro palpite não é, dado que não estamos criando um novo MemoryStream (). Eu apreciaria uma resposta. thx
Ehsan Najafi
16

Você pode retornar o FileResult com estes métodos:

1: Retornar 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: Return 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
fonte
2
Se dentro de um, ControllerBaseexistem muitas versões sobrecarregadas do ControllerBase.Filehelper que retornam qualquer uma delas.
Nkosi
2
Sua resposta ainda é válida. Portanto, não se sinta desanimado. Eu estava apenas apontando alguns recursos que você pode usar para apoiar sua resposta.
Nkosi
1
Sim isso é verdade.
Hamed Naeemaei
9

Aqui está um exemplo simplista de streaming de um arquivo:

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:

Certifique-se de usar FileStreamResultde Microsoft.AspNetCore.Mvce não de System.Web.Mvc.

Gpresland
fonte