WebRTC Transferência de arquivos: Browser-to-Browser Tutorial
Transfira ficheiros diretamente entre browsers usando WebRTC. Data channels, signaling e configuração de peer connection para partilha de ficheiros em tempo real.
A transferência de ficheiros por WebRTC move bytes diretamente entre dois browsers usando um RTCDataChannel, sem nenhum servidor no caminho dos dados após o handshake inicial. Os ingredientes necessários: um canal de signaling (WebSocket ou qualquer pequeno relay) para trocar ofertas SDP e candidatos ICE, servidores STUN para descoberta de NAT, um fallback TURN para NATs simétricos, e um data channel configurado para entrega fiável e ordenada. Uma vez que a peer connection está ativa, usa-se channel.send() com fragmentos de 16 KB-256 KB e os bytes fluem por uma associação SCTP cifrada com DTLS 1.2 a velocidade próxima da linha de rede.
Como o WebRTC Realmente Obtém Ligação Peer-to-Peer
O WebRTC não é magia — é ICE mais SDP mais DTLS mais SCTP empilhados juntos. O remetente cria um RTCPeerConnection, abre um data channel, gera uma oferta SDP, e envia-a ao destinatário via o canal de signaling. O destinatário responde. Ambos os lados depois trocam candidatos ICE (IP local, IP reflexivo via STUN, IP de relay via TURN) até encontrarem um caminho funcional. O DTLS 1.2 faz handshake ponta a ponta, o SCTP corre por cima para streaming fiável, e os bytes começam a fluir.
A cifração é obrigatória e integrada. Não pode ser desativada. Isso é uma vitória de segurança significativa — ao contrário dos WebSockets, não precisa de se lembrar de envolver nada em crypto ao nível da aplicação para proteger contra escutas na rede. Para E2EE real contra o próprio servidor de signaling, adicione uma segunda ronda de AES-256-GCM por cima, porque um servidor de signaling malicioso poderia trocar o seu próprio certificado DTLS.
Signaling: A Parte Que o WebRTC Não Define
O WebRTC deliberadamente deixa o signaling para si. Um WebSocket no servidor é suficiente; também funciona um código de sala partilhado publicado numa Firebase realtime DB, ou mesmo strings SDP coladas manualmente. O que importa é que ambos os pares eventualmente troquem uma oferta, uma resposta e um fluxo de candidatos ICE.
Servidor de signaling mínimo em Node:
const rooms = new Map();
wss.on('connection', (ws) => {
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'join') {
const room = rooms.get(msg.room) ?? new Set();
room.add(ws); rooms.set(msg.room, room);
} else {
for (const peer of rooms.get(msg.room) ?? []) {
if (peer !== ws) peer.send(raw);
}
}
});
});
Menos de 20 linhas. O servidor nunca vê bytes de ficheiros — apenas metadados SDP e ICE. Pode alojar isto num VPS de 5 EUR ou Cloudflare Workers e servir centenas de transferências simultâneas.
Configurar a Peer Connection
Crie a ligação com o STUN público do Google mais um fallback TURN:
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'turn:turn.example.com:3478',
username: 'user', credential: 'pass' }
]
});
Cerca de 15-25% das ligações residenciais estão atrás de NATs simétricos que o STUN não consegue atravessar, por isso o TURN não é opcional para um serviço de produção. Execute coturn num VPS com largura de banda suficiente para gerir o tráfego relayed, ou pague por um fornecedor TURN gerido como Xirsys ou Twilio.
Abra o data channel no lado do ofertante antes de criar a oferta:
const channel = pc.createDataChannel('file', {
ordered: true, maxRetransmits: null
});
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signaling.send({ type: 'offer', sdp: offer.sdp });
Ordered + retransmissões ilimitadas dá fiabilidade equivalente ao TCP. O modo unordered é mais rápido mas precisa de reagrupamento ao nível da aplicação.
Fragmentar o Ficheiro para o Canal
O SCTP tem um limite prático de 256 KB por mensagem, e browsers mais antigos têm dificuldade acima de 16 KB. Um padrão seguro são fragmentos de 16 KB lidos do ficheiro via File.slice():
const chunkSize = 16 * 1024;
let offset = 0;
channel.bufferedAmountLowThreshold = 1024 * 1024;
function sendNext() {
while (offset < file.size && channel.bufferedAmount < 4 * 1024 * 1024) {
const chunk = file.slice(offset, offset + chunkSize);
chunk.arrayBuffer().then((buf) => channel.send(buf));
offset += chunkSize;
}
}
channel.onbufferedamountlow = sendNext;
sendNext();
O watermark de bufferedAmount impede de enfileirar gigabytes no buffer de envio do SCTP e ficar sem memória. Quando o buffer desce abaixo de 1 MB, reabastece para 4 MB. Isto dá débito próximo de 40-80 MB/s em redes locais e 5-20 MB/s em banda larga residencial típica.
Receber e Escrever Bytes para o Disco
No lado que responde, escute o canal de entrada:
pc.ondatachannel = ({ channel }) => {
const chunks = [];
let received = 0;
channel.onmessage = ({ data }) => {
chunks.push(data);
received += data.byteLength;
updateProgress(received);
if (received === expectedSize) finish(chunks);
};
};
Para ficheiros maiores que 500 MB, não acumule em RAM. Use a File System Access API para fazer streaming diretamente para o disco:
const handle = await window.showSaveFilePicker({ suggestedName: fileName });
const writable = await handle.createWritable();
channel.onmessage = async ({ data }) => writable.write(data);
O Firefox e Safari ainda não suportam showSaveFilePicker, por isso faça fallback para Blob + URL.createObjectURL para download nesses browsers, limitado a 2 GB.
Enviar Metadados do Ficheiro Antes dos Bytes
O receptor precisa de saber o nome do ficheiro, tamanho e tipo MIME antes de o fluxo de bytes começar. Use um pequeno handshake JSON no data channel:
channel.send(JSON.stringify({
type: 'metadata', name: file.name,
size: file.size, mime: file.type, sha256: fileHash
}));
Depois mude para modo binário. O receptor alterna com base em se data é uma string ou ArrayBuffer. Inclua um SHA-256 do ficheiro para verificação de integridade após a transferência, e opcionalmente inclua uma impressão digital da chave se estiver a adicionar AES-GCM ao nível da aplicação por cima.
Tratar Falhas de Ligação
Os data channels WebRTC falham de três formas: o ICE nunca completa (NAT, firewall bloqueia), o handshake DTLS falha (desvio de relógio, problemas de certificado), ou a ligação cai a meio da transferência (laptop em modo de suspensão, mudança de rede). Escute pc.oniceconnectionstatechange e actue em 'failed' ou 'disconnected'. O Chrome mantém 'disconnected' por alguns segundos antes de passar para 'failed'; o Safari é menos paciente.
Em caso de falha a meio da transferência, reinicie o ICE sem reconstruir toda a ligação:
await pc.restartIce();
const offer = await pc.createOffer({ iceRestart: true });
// reenviar via signaling
Se o restart falhar, faça fallback para upload resumível via servidor — uma arquitetura híbrida P2P + servidor. Algumas ferramentas de transferência WebRTC como Wormhole e justbeamit usam este padrão porque trata os 15% de condições de rede onde P2P puro simplesmente não funciona.
Adicionar Cifração Ponta a Ponta Contra o Próprio Servidor
O DTLS integrado do WebRTC protege contra atacantes de rede mas não contra um servidor de signaling malicioso ou comprometido. Para E2EE verdadeiro, ambos os pares geram um par de chaves ECDH P-256, trocam chaves públicas via um código curto out-of-band (QR ou frase de 6 palavras), derivam um segredo partilhado via HKDF-SHA256, e encriptam cada mensagem do data channel com AES-256-GCM antes de chamar send. Assim, mesmo que o servidor de signaling troque certificados DTLS, não consegue ler os ficheiros.
O HexaTransfer usa uma arquitetura híbrida — texto cifrado armazenado no servidor com AES-256-GCM do lado do cliente, em vez de P2P — trocando directidade por partilha tolerante a offline. Experimente em hexatransfer.com — gratuito, sem conta necessária, máximo de 10 GB.
Onde o WebRTC Ganha e Perde
A transferência de ficheiros por WebRTC brilha quando ambos os pares estão online simultaneamente, quando a privacidade face ao próprio servidor importa, e quando os ficheiros são suficientemente grandes (100 MB+) para que os custos de largura de banda de relay sejam dolorosos. Perde quando os utilizadores querem enviar e sair, quando os destinatários abrem a ligação horas depois, ou quando os destinatários estão em redes corporativas restritivas que bloqueiam STUN e TURN. Para uma ferramenta de transferência de uso geral, P2P puro cobre talvez 60% dos casos de uso confortavelmente. Os outros 40% precisam de um fallback com suporte de servidor — é por isso que quase todos os produtos de "transferência P2P de ficheiros" têm um relay na arquitetura algures.
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