Upload e download de arquivos no ASP.NET Core: formulário, Ajax e componentes

Uma das tarefas mais comuns em aplicações web é o envio e a recuperação de arquivos entre cliente e servidor. No ASP.NET Core, o processo é simplificado pela interface IFormFile, que representa um arquivo recebido via requisição multipart/form-data.

  1. Upload por model binding

O jeito mais direto é criar um formulário HTML com enctype="multipart/form-data" e usar a tag helper asp-controller/asp-action. O atributo multiple permite selecionar vários arquivos simultaneamente.

<form method="post" enctype="multipart/form-data" asp-controller="Files" asp-action="Upload">
   <input type="file" name="attachments" multiple />
   <button type="submit">Enviar</button>
</form>

No controller, basta declarar uma lista de IFormFile como parâmetro. O ASP.NET Core se encarrega de preeenchê-la com os arquivos enviados.

public class FilesController : Controller
{
   private readonly IWebHostEnvironment _env;

   public FilesController(IWebHostEnvironment env)
   {
       _env = env;
   }

   [HttpPost]
   public async Task<IActionResult> Upload(List<IFormFile> attachments)
   {
       if (attachments == null || attachments.Count == 0)
           return BadRequest("Nenhum arquivo foi selecionado.");

       long totalBytes = 0;

       foreach (var file in attachments)
       {
           if (file.Length == 0) continue;

           var extension = Path.GetExtension(file.FileName).TrimStart('.');
           var uniqueName = $"{Guid.NewGuid():N}.{extension}";
           var destination = Path.Combine(_env.WebRootPath, "uploads", uniqueName);

           Directory.CreateDirectory(Path.GetDirectoryName(destination)!);

           await using var stream = System.IO.File.Create(destination);
           await file.CopyToAsync(stream);

           totalBytes += file.Length;
       }

       return Ok(new { count = attachments.Count, bytes = totalBytes });
   }
}

A interface IFormFile expõe as propriedades essenciais do arquivo e os métodos para ler o conteúdo como stream.

public interface IFormFile
{
   string ContentType { get; }
   string ContentDisposition { get; }
   IHeaderDictionary Headers { get; }
   long Length { get; }
   string Name { get; }
   string FileName { get; }
   Stream OpenReadStream();
   void CopyTo(Stream target);
   Task CopyToAsync(Stream target, CancellationToken cancellationToken = default);
}
  1. Upload via Ajax com FormData

Para anviar arquivos sem recarregar a página, utilize um objeto FormData e envie a requisição via fetch. A ação do servidor permanece a mesma do exemplo anterior.

<form id="formUpload">
   <input type="file" name="attachments" multiple />
   <button type="button" onclick="enviarArquivos()">Enviar via Ajax</button>
</form>
async function enviarArquivos() {
   const form = document.getElementById('formUpload');
   const payload = new FormData(form);

   try {
       const response = await fetch('/files/upload', {
           method: 'POST',
           body: payload
       });

       if (!response.ok) throw new Error('Falha no envio");

       const result = await response.json();
       console.log(result);
   } catch (error) {
       console.error(error);
   }
}

Se o nome do parâmetro no controller não corresponder ao name dos inputs, a lista pode ficar vazia, mas os arquivos ainda estarão disponíveis em Request.Form.Files.

  1. Integração com componentes de upload

Bibliotecas como WebUploader, Dropzone ou Uppy funcionam da mesma forma: elas constroem uma requisição multipart no cliente e a anviam para uma ação que lê IFormFile ou Request.Form.Files.

<div id="uploadContainer"></div>
function inicializarUploader() {
   const uploader = new UploaderComponent({
       container: '#uploadContainer',
       endpoint: '/files/upload',
       autoUpload: true,
       maxFiles: 5
   });

   uploader.onComplete = (response) => {
       console.log('Upload finalizado:', response);
   };
}
  1. Download seguro via stream

Expor o caminho físico dos arquivos via URL direta não é seguro. A prática recomendada é receber um identificador, abrir o arquivo como stream e devolver o resultado com o Content-Type correto.

public IActionResult Download(string path)
{
   if (string.IsNullOrWhiteSpace(path))
       return BadRequest("Caminho não informado.");

   var fullPath = Path.Combine(_env.WebRootPath, "uploads", path);

   if (!System.IO.File.Exists(fullPath))
       return NotFound();

   var provider = new FileExtensionContentTypeProvider();

   if (!provider.TryGetContentType(fullPath, out var contentType))
       contentType = "application/octet-stream";

   var stream = new FileStream(fullPath, FileMode.Open, FileAccess.Read);
   return File(stream, contentType, Path.GetFileName(fullPath));
}

No ASP.NET Core, FileExtensionContentTypeProvider substitui o antigo MimeMapping.GetMimeMapping, removendo a dependência do System.Web.

<input type="text" id="caminhoArquivo" placeholder="nome.extensão" />
<button onclick="baixarArquivo()">Baixar</button>
function baixarArquivo() {
   const nome = document.getElementById('caminhoArquivo').value;
   window.location.href = '/files/download?path=' + encodeURIComponent(nome);
}

Tags: ASP.NET Core IFormFile FileExtensionContentTypeProvider MVC WebUploader

Publicado em 6-27 00:25