Skip to main content

Envio de bytes, arquivos, vídeos, imagem, áudio | Multipart Upload | Arquitetura

  • Envio de bytes, arquivos, vídeos, imagem, áudio | Multipart Upload | Arquitetura

Protocolos

Vídeos

  • Dash
  • HLS

Detalhes

  • Para arquivos devemos usar um storage como o AWS S3
  • Independentemente do tamanho do arquivo, devemos sempre enviar do front-end direto para o storage
    • O envio acontecerá quebrado. Por exemplo: A AWS forneçe urls pré-assinadas que você quebra o seu arquivo dividindo ele em partes menores e envia cada parte em uma url pré-assinada
info

Em arquivos muito grande, o multipart é obrigatório

Exemplo do Front

const PART_SIZE = 50 * 1024 * 1024; // 50MB
const MAX_PARALLEL = 5;

async function uploadLargeVideo(file) {
// 1. Inicia multipart upload
const initResponse = await fetch("/api/multipart/init", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
fileName: file.name,
contentType: file.type
})
});

const { uploadId, key } = await initResponse.json();

const parts = Math.ceil(file.size / PART_SIZE);
const uploadedParts = [];

const queue = [];

for (let partNumber = 1; partNumber <= parts; partNumber++) {
const start = (partNumber - 1) * PART_SIZE;
const end = Math.min(start + PART_SIZE, file.size);
const blob = file.slice(start, end);

queue.push(() =>
uploadPart(blob, partNumber, uploadId, key).then(etag => {
uploadedParts.push({ ETag: etag, PartNumber: partNumber });
})
);
}

await runParallel(queue, MAX_PARALLEL);

// 3. Finaliza upload
await fetch("/api/multipart/complete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
uploadId,
key,
parts: uploadedParts
})
});

console.log("Upload finalizado");
}

/*-------------*/
async function uploadPart(blob, partNumber, uploadId, key) {
const response = await fetch("/api/multipart/presign", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
uploadId,
key,
partNumber
})
});

const { url } = await response.json();

const uploadResponse = await fetch(url, {
method: "PUT",
body: blob
});

if (!uploadResponse.ok) {
throw new Error("Erro ao enviar parte " + partNumber);
}

return uploadResponse.headers.get("ETag");
}

/*-------------*/

async function runParallel(tasks, limit) {
const executing = [];

for (const task of tasks) {
const p = task();
executing.push(p);

if (executing.length >= limit) {
await Promise.race(executing);
executing.splice(
executing.findIndex(e => e === p),
1
);
}
}

return Promise.all(executing);
}


/*-------------*/

xhr.upload.onprogress = (event) => {
const percent = (event.loaded / event.total) * 100;
};