Download de arquivos
Mecanismo de saída de binário pro usuário: o servidor gera o arquivo
(geralmente via exportação) e o client dispara o download no navegador. No
HTD-Front isso é feito por uma única função genérica — ela não sabe se é
PDF, Excel ou outro formato, só recebe o Blob pronto e o nome do arquivo.
O problema que isso resolve
Exportações (relatórios em PDF/Excel) nascem no servidor como um Blob. O
navegador não baixa um Blob sozinho — é preciso transformá-lo numa URL
temporária e simular um clique num link. Ter esse passo duplicado em cada
tela de relatório espalharia a mesma lógica frágil (e o vazamento de memória
por esquecer de revogar a URL) por todo o projeto. Por isso existe
lib/download-arquivo.ts: um lugar só.
A função: baixarArquivo (lib/download-arquivo.ts)
export function baixarArquivo(blob: Blob, filename: string) {
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
URL.createObjectURL(blob)cria uma URL temporária (blob:...) que aponta pro binário na memória do navegador.<a download>força o navegador a baixar (e sugerirfilename) em vez de tentar abrir o tipo no próprio browser.URL.revokeObjectURL(url)libera a memória — obrigatório, senão cada download vaza uma referência até recarregar a página. Por isso a função faz tudo num lugar e ninguém precisa se lembrar desse detalhe.
O padrão de ponta a ponta
Os serviços de relatório devolvem o Blob cru da API e repassam pra essa
função — a tela nunca monta a URL nem o <a>:
// services/api-relatorio-pdf.ts
export async function gerarRelatorioPdf(template, dados): Promise<Blob> {
const response = await fetch("/api/relatorios/pdf", { method: "POST", /* ... */ });
if (!response.ok) throw new Error("Não foi possível gerar o PDF do relatório.");
return response.blob();
}
// na tela (Client Component)
const blob = await gerarRelatorioUsuariosPdf(payload); // Promise<Blob>
baixarArquivo(blob, "relatorio-usuarios.pdf");
Quem chama decide o filename (vem do próprio payload de dados do relatório,
ex.: "relatorio-usuarios.pdf"); a função de download só respeita o que
recebe. Detalhe de cada formato em
Estilos de PDF e
Estilos de Excel.
Quando usar
- Download de binário vindo da API (relatórios, anexos, exports): sempre
baixarArquivo(blob, nome). - Imagem que já tem URL (ex.:
imagem_fundo_login_urlda identidade institucional): não é download — é só<img src={url}>. OBlob/object URL só entra quando o dado nasce no client ou vem como resposta binária. - Preview antes de baixar: nenhuma exportação dispara o download direto no clique — PDF abre em modal de preview, Excel numa página de preview dos dados de origem (ver Arquivos).
Convenção do projeto
baixarArquivoé a única forma de baixar umBlobno client — telas não criamURL.createObjectURLpor conta própria.- O
filenameé definido por quem pede o relatório, nunca hardcoded dentro delib/download-arquivo.ts. - Revogar a object URL é responsabilidade da função, não de quem chama.
Leitura de apoio
- MDN — URL.createObjectURL — ciclo de vida da object URL e por que revogar.
- MDN — HTMLAnchorElement.download — forçar download em vez de navegação.
- MDN — Blob — o tipo retornado por
response.blob().

