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