.NET

4 jan, 2019

ASP .NET MVC – Retornando arquivos de uma Web API

Publicidade

Hoje veremos como retornar arquivos a partir de uma Web API usando a ASP .NET MVC 5.

Uma Web API pode ser vista como um conjunto de serviços expostos via web com o objetivo de integrar sua aplicação a diversos tipos de clientes que queiram usar os serviços. Esses serviços são usados como requisições HTTP e retornam uma resposta em um formato específico como XML, JSON, etc.

Essa resposta pode ser uma informação de um repositório de dados, o retorno de uma operação, etc. O cliente pode ser uma página web, uma aplicação desktop, uma aplicação mobile, etc.

Dessa forma, uma Web API é uma API sobre a web (serviços REST HTTP), sendo que a ASP.NET Web API é um framework que permite que você construa Web APIs. Ou seja, serviços baseados em HTTP no topo do .NET Framework usando uma convenção de base e um modelo de programação semelhante ao da ASP.NET MVC.

Quando estamos trabalhando com o serviço REST, é muito importante entender como enviar arquivos. Neste artigo, vamos discutir como retornar arquivos nos formatos PDF, Word, Excel, Zip, etc, a partir de um serviço ASP .NET Web API.

Vamos supor que tenhamos como requisito enviar um arquivo com base no tipo de arquivo fornecido para a solicitação de serviço. Por exemplo, quando enviamos o tipo de arquivo como PDF, o serviço retornará o arquivo PDF. Se enviarmos um tipo Doc, o serviço retornará o documento do Word. Se for o tipo zip, enviaremos um arquivo zipado, etc.

Não podemos apenas enviar o arquivo diretamente. Antes, temos que realizar alguns procedimentos, que são descritos a seguir:

  • Converter o arquivo a ser enviado em um array de bytes: podemos usar a classe File do namespace System.IO para converter diversos tipos de arquivos;
  • Adicionar os bytes a um MemoryStream: usamos a classe MemoryStream() e criamos uma instância dessa classe passando os bytes do arquivo;
  • Adicionar o objeto MemoryStream gerado a um Conteúdo HttpResponseMessage: usamos a propriedade Content da instância HttpResponseMessage;
  • Ajustar os Headers HttpResponseMessage para o cliente poder receber o arquivo: precisamos definir as propriedades: ContentDisposition, FileName e ContentType;

A propriedade ContentType tem um papel muito importante no envio dos arquivos. O cabeçalho Content-Type é utilizado para indicar o tipo de arquivo do recurso.

Para saber os tipos MIME suportados, consulte este link: https://developer.mozilla.org/pt-BR/docs/Web/HTTP/Basico_sobre_HTTP/MIME_types/Complete_list_of_MIME_types

Há dois tipos de MIME que são importantes para tipos padrões:

  • text/plain: tipo padrão para arquivos de texto. Um arquivo de texto deve ser legível para um ser humano, e não deve conter dados binários.
  • application/octet-stream: tipo padrão para todos os outros casos. Um tipo de arquivo desconhecido deveria usar este tipo. Os navegadores tomarão mais cuidado ao manipular esses arquivos, tentando proteger o usuário e prevenir comportamentos perigosos.

No nosso exemplo vou usar o valor application/octet-strem para essa propriedade, pois vamos querer enviar tipos diferentes de arquivos e, porque conhecemos e confiamos no conteúdo que estamos disponibilizando.

Então mãos à obra!

Criando a Web API no VS 2017

  • Abra o VS 2017 Community e selecione File > New Project (ou crie apenas um arquivo HTML);
  • Selecione Web e o template ASP .NET Web Application e informe o nome WebAPI_Arquivos e clique em OK;

A seguir, marque a opção Empty e Web API e clique no botão OK:

Com isso, teremos um projeto pronto, conforme a figura a seguir, onde criaremos a nossa Web API.

Clique com o botão direito sobre a pasta Controllers e a seguir clique em Add > Controller e selecione a opção Web API 2 Controller – Empty e clique no botão Add:

Informe o nome ArquivosController.

A seguir, inclua o código abaixo neste controlador:

using System.IO;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebAPI_Arquivos.Controllers
{
    public class ArquivosController : ApiController
    {
        string arquivo_Pdf = @"C:\Dados\PDF\teste.pdf";
        string arquivo_xls = @"C:\Dados\XLS\teste.xls";
        string arquivo_doc = @"C:\Dados\DOC\teste.docx";
        string arquivo_zip = @"C:\Dados\ZIP\teste.zip";
        [HttpGet]
        [ActionName("BaixarArquivo")]
        public IHttpActionResult BaixarArquivo(string formato)
        {
            string reqArquivo = formato.ToLower() == "pdf" ? arquivo_Pdf : (formato.ToLower() == "xls" ? arquivo_xls
 : (formato.ToLower() == "doc" ? arquivo_doc : arquivo_zip));
            string arquivoNome = "teste." + formato.ToLower();
            //converte arquivos em um array de bytes
            var dataBytes = File.ReadAllBytes(reqArquivo);
            //adiciona bytes ao memory stream   
            var dataStream = new MemoryStream(dataBytes);
            return new ArquivoResult(dataStream, Request, arquivoNome);
        }
    }
    public class ArquivoResult : IHttpActionResult
    {
        MemoryStream arquivoStuff;
        string nomeDoArquivo;
        HttpRequestMessage httpRequestMessage;
        HttpResponseMessage httpResponseMessage;

        public arquivoResult(MemoryStream data, HttpRequestMessage request, string filename)
        {
            arquivoStuff = data;
            httpRequestMessage = request;
            nomeDoArquivo = filename;
        }

        public System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAsync(System.Threading
.CancellationToken cancellationToken)
        {
            httpResponseMessage = httpRequestMessage.CreateResponse(HttpStatusCode.OK);
            httpResponseMessage.Content = new StreamContent(arquivoStuff);
            httpResponseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.
ContentDispositionHeaderValue("attachment");
            httpResponseMessage.Content.Headers.ContentDisposition.FileName = nomeDoArquivo;
            httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers
.MediaTypeHeaderValue("application/octet-stream");
            return System.Threading.Tasks.Task.FromResult(httpResponseMessage);
        }
    }
}

No código estamos definindo os arquivos que desejamos servir e a seguir criamos o método Action BaixarArquivo, definido como um GET que vai receber como parâmetro o tipo de arquivo a ser baixado.

No método ArquivoResult realizarmos o processamento do download definindo o cabeçalho para o cliente. Para poder baixar os arquivos, temos que definir o formato do mesmo via URL, e para isso vamos ajustar o arquivo WebApiConfig definindo o parâmetro opcional com o nome de formato:


using System.Web.Http;
namespace WebAPI_Arquivos
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Web API routes
            config.MapHttpAttributeRoutes();
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{formato}",
                defaults: new { formato = RouteParameter.Optional }
            );
        }
    }
}

Após isso, basta informar na URL a chamada no seguinte formato: servidor:porta/api/Arquivos/BaixarArquivo?formato={doc,xls,pdf,zip}

Exemplos:

  • localhost:20263/api/Arquivos/BaixarArquivo?formato=pdf
  • localhost:20263/api/Arquivos/BaixarArquivo?formato=doc
  • localhost:20263/api/Arquivos/BaixarArquivo?formato=xls

Executando o projeto iremos obter o seguinte resultado:

Podemos melhorar o projeto tornando-o mais amigável, mas o objetivo principal era retornar os arquivos definidos.

Pegue o projeto aqui: WebAPI_Arquivos.zip (sem as referências).