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