Ir para o conteúdo
HexaTransfer
Voltar ao blog
Analises tecnicas

Crie Your Own Transferência de arquivos Service: Dev Tutorial

Construa a arquivo transfer service from scratch. Backend API design, storage integration, e user interface development tutorial.

Para construir o seu próprio serviço de transferência de ficheiros, precisa de cinco componentes: um frontend que fatia e opcionalmente encripta ficheiros no browser, uma API REST que cria sessões de upload e pré-assina URLs de armazenamento, armazenamento de objetos compatível com S3 (Cloudflare R2, Backblaze B2, ou MinIO), uma pequena base de dados de metadados (SQLite ou Postgres), e um reverse proxy com TLS 1.3. Com Node 22, Fastify e Vite, pode lançar um serviço ao estilo WeTransfer em cerca de 600 linhas de código e hospedar tudo por 5 a 15 euros por mês com tráfego moderado.

Escolher Primeiro a Camada de Armazenamento

A economia do armazenamento pode fazer ou desfazer um serviço de transferência de ficheiros. O AWS S3 cobra $0,09 por GB de egresso, o que significa que um download popular de 2 GB partilhado 1.000 vezes custa $180 só em largura de banda. O Cloudflare R2 cobra zero de egresso e $0,015 por GB armazenado. O Backblaze B2 cobra $0,01 por GB de egresso e $0,006 por GB armazenado. Para um serviço de transferência gratuito, o R2 é a escolha óbvia. O MinIO permite auto-hospedar semântica S3 no seu próprio hardware, o que funciona se já tiver largura de banda e discos disponíveis.

Use URLs pré-assinadas para que os clientes façam upload e download diretamente do armazenamento, nunca através do seu servidor. Isto mantém a sua API limitada pela CPU em vez de pela largura de banda.

Esboço da Superfície da API

Mantenha a API pequena. Cinco endpoints cobrem 95% do que precisa:

  • POST /sessions — criar uma sessão de upload, devolver ID e URLs multipart pré-assinadas
  • POST /sessions/:id/complete — finalizar um upload multipart, devolver URL de partilha
  • GET /shares/:id — devolver metadados do ficheiro (tamanho, expiração, downloads restantes)
  • GET /shares/:id/download — devolver uma URL de download pré-assinada
  • DELETE /shares/:id — permitir que o proprietário revogue antecipadamente (requer um token de revogação)

Limite de taxa POST /sessions de forma agressiva — 20 por IP por hora é suficiente. Todos os endpoints devolvem JSON, usam Cache-Control: no-store, e definem Content-Security-Policy nas páginas HTML.

Ligação do Backend com Fastify

O Fastify fornece validação de esquema JSON, logging sensato e 30k req/s por core sem esforço. Um POST /sessions mínimo:

fastify.post('/sessions', {
  schema: {
    body: {
      type: 'object', required: ['size'],
      properties: {
        size: { type: 'integer', minimum: 1, maximum: 10737418240 },
        contentType: { type: 'string', maxLength: 120 }
      }
    }
  }
}, async (req) => {
  const id = nanoid(16);
  const key = `blobs/${id}`;
  const upload = await s3.createMultipartUpload({
    Bucket: 'transfers', Key: key, ContentType: req.body.contentType
  });
  const partCount = Math.ceil(req.body.size / (8 * 1024 * 1024));
  const urls = await Promise.all(
    Array.from({ length: partCount }, (_, i) =>
      getSignedUrl(s3, new UploadPartCommand({
        Bucket: 'transfers', Key: key, UploadId: upload.UploadId, PartNumber: i + 1
      }), { expiresIn: 3600 }))
  );
  db.prepare('INSERT INTO sessions VALUES (?, ?, ?, ?)')
    .run(id, upload.UploadId, key, Date.now());
  return { id, uploadId: upload.UploadId, partUrls: urls };
});

Teto de 10 GB, partes de 8 MB, validade de URL de 1 hora. Ajuste para o seu mix de tráfego.

Construção da Interface de Upload

Vite mais React mais zero biblioteca de UI chega a um drag-and-drop funcional em cerca de 150 linhas. A peça crítica é o worker de upload: lance um Web Worker dedicado, passe-lhe o handle do File e os URLs de parte, e peça-lhe para fazer PUT de cada chunk com concorrência de 3 vias. A thread principal mantém-se responsiva, as atualizações de progresso chegam via postMessage.

// worker.js
self.onmessage = async ({ data: { file, partUrls } }) => {
  const partSize = 8 * 1024 * 1024;
  const etags = [];
  for (let i = 0; i < partUrls.length; i++) {
    const blob = file.slice(i * partSize, (i + 1) * partSize);
    const res = await fetch(partUrls[i], { method: 'PUT', body: blob });
    etags[i] = res.headers.get('etag');
    self.postMessage({ type: 'progress', done: i + 1, total: partUrls.length });
  }
  self.postMessage({ type: 'done', etags });
};

Adicione um campo de senha que derive uma chave via PBKDF2 (600.000 iterações, SHA-256) e passe cada chunk por AES-256-GCM antes do PUT. Isto transforma o seu serviço num serviço com encriptação ponta-a-ponta em vez de encriptação do lado do servidor.

Design do Esquema da Base de Dados

O SQLite via better-sqlite3 trata dezenas de milhões de linhas sem dificuldade. Mantenha o esquema enxuto:

CREATE TABLE sessions (
  id TEXT PRIMARY KEY, upload_id TEXT, object_key TEXT, created_at INTEGER
);
CREATE TABLE shares (
  id TEXT PRIMARY KEY, object_key TEXT, size_bytes INTEGER,
  content_type TEXT, expires_at INTEGER, max_downloads INTEGER,
  download_count INTEGER DEFAULT 0, revocation_token TEXT
);
CREATE INDEX idx_shares_expires ON shares(expires_at);

Sem tabela de utilizadores, sem email, sem logs de IP. Esta é uma escolha deliberada — torna a conformidade com o RGPD quase trivial. Um cron noturno elimina as linhas expiradas e os seus objetos S3:

const expired = db.prepare('SELECT object_key FROM shares WHERE expires_at < ?').all(Date.now());
for (const { object_key } of expired) {
  await s3.deleteObject({ Bucket: 'transfers', Key: object_key });
}
db.prepare('DELETE FROM shares WHERE expires_at < ?').run(Date.now());

Implementação de Links de Partilha e Downloads

Os URLs de partilha têm o aspeto https://suaapp.com/f/abc123#k=<base64key> quando adicionou encriptação do lado do cliente. O fragmento mantém a chave fora dos logs do servidor. A página de download busca /shares/abc123, obtém uma URL S3 pré-assinada, transmite bytes diretamente do R2, desencripta no browser, e entrega o resultado ao StreamSaver.js ou à File System Access API.

Defina Content-Disposition: attachment; filename*=UTF-8''nome-codificado para que o browser acione um diálogo de gravação em vez de renderizar o ficheiro inline. Isto importa para PDFs, ficheiros HTML e SVG, todos os quais podem executar scripts num contexto de browser de outra forma.

Proteção Contra Abusos

Os serviços de transferência de ficheiros atraem abusos. Espere três categorias: distribuição de malware, hospedagem de payloads de phishing e uploads com violação de direitos de autor. Mitigações por ordem de impacto: limite de taxa de uploads por IP, exija um tamanho mínimo (abaixo de 10 KB é quase sempre lixo), calcule um SHA-256 do ciphertext e verifique contra uma lista de hashes conhecidos como maus, e forneça um formulário de denúncia de abuso sem fricção. O ClamAV em conteúdo desencriptado só funciona quando tem a chave, o que derrota a encriptação ponta-a-ponta — a resposta correta são limites de taxa agnósticos ao conteúdo mais remoções responsivas.

Assine cada URL pré-assinada com um TTL curto (máximo 1 hora) e ligue as sessões a um token efémero gerado pelo browser armazenado num cookie Secure; HttpOnly; SameSite=Strict.

Deployment e Operação a Baixo Custo

Um único Hetzner CX22 de $10 (2 vCPU, 4 GB RAM) corre Fastify + SQLite + um reverse proxy Caddy e trata de milhares de utilizadores ativos. O Caddy dá-lhe TLS 1.3 automaticamente via Let's Encrypt. Coloque a Cloudflare à frente para proteção DDoS e o WAF gratuito. Monitorize com Uptime Kuma (gratuito, auto-hospedado) e registe em stdout capturado pelo journald.

Para redundância geográfica, replique o SQLite com Litestream para R2, e aponte uma segunda região para o mesmo armazenamento de objetos. O failover é ao nível do DNS. Toda a sua história de recuperação de desastres cabe num guardanapo.

O que Deixar de Fora até Precisar

Não construa contas, equipas ou pastas na v1. Não adicione entrega de email. Não implemente pré-visualização de vídeo ou miniaturas de imagens (ambas forçam o servidor a ver o plaintext). Não lance uma aplicação móvel antes de a versão web ser sólida no Safari móvel. O HexaTransfer é construído essencialmente nesta abordagem — API Fastify, armazenamento R2, Web Crypto no browser, metadados SQLite, sem contas. A proliferação de funcionalidades que destruiu os clones antigos de transferência de ficheiros foi sempre o mesmo padrão: contas, depois quotas de armazenamento, depois pagamento, depois planos de equipa, e de repente estão a competir com a Dropbox em vez de lançar uma ferramenta de transferência rápida e privada.

Experimente em https://hexatransfer.com — gratuito, sem conta, máximo de 10 GB.

Envie arquivos grandes com segurança e criptografia de ponta a ponta

Transfira arquivos de até 10 GB gratuitamente com criptografia de ponta a ponta. Sem necessidade de conta. Seus arquivos são criptografados no navegador antes do envio — ninguém mais pode lê-los.

Enviar um arquivo