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).