Exemplo de Implementação do Protocolo Tus
Tus é um protocolo aberto para uploads de arquivos retomáveis. O protocolo permite retomar uploads após uma queda de conexão, enviar arquivos grandes em partes e controlar o processo de upload.
Quando você precisa do Tus
Use o Tus se você estiver fazendo upload de arquivos grandes e quiser:
- Retomar uploads após uma queda de rede — o usuário não perderá o progresso
- Enviar o arquivo em partes (chunk upload) — mais confiável para arquivos grandes
- Mostrar progresso para o usuário e gerenciar tentativas
Para desenvolvedores: Documentação oficial do protocolo: https://tus.io/protocols/resumable-upload.html
Bibliotecas para diferentes linguagens: https://tus.io/implementations.html
Como funciona o upload Tus (4 passos)
- O cliente seleciona um arquivo e faz uma solicitação ao seu backend (por exemplo,
POST /upload), passando o nome do arquivo e o tamanho (sem o arquivo em si). - O backend chama a API do Kinescope
POST /v2/inite recebe um endpoint Tus único em resposta. - O backend retorna este endpoint para o cliente (como redirecionamento ou em JSON).
- O cliente faz upload do arquivo via protocolo Tus diretamente para o Kinescope usando o endpoint recebido.
init (para obter um endpoint Tus único), e então o navegador faz o upload do arquivo diretamente para o Kinescope via endpoint emitido.O que preparar
Antes de começar, certifique-se de ter:
- Token de API do Kinescope (armazene no servidor, não publique no navegador)
- ID do projeto ou pasta (
parent_id) para onde o arquivo será enviado - Seu endpoint de backend, por exemplo,
POST /upload, que irá:- aceitar metadados do arquivo do cliente
- chamar
https://uploader.kinescope.io/v2/init - retornar o endpoint Tus para o cliente
Diagrama de interação
Aqui está como parece o processo de upload:
sequenceDiagram
participant Client as Navegador_Cliente
participant Backend as Backend_ServidorUsuario
participant API as API_Kinescope
participant TusEndpoint as Endpoint_Tus_Kinescope
Note over Backend: "Arquivo não passa pelo backend"
Client->>Backend: "POST /upload (metadados, tamanho)"
Backend->>API: "POST /v2/init (Bearer TOKEN, parent_id, filename, filesize, type)"
API-->>Backend: "201 Created (endpoint)"
Backend-->>Client: "endpoint (Location/redirect ou JSON)"
Note over Client,TusEndpoint: "Arquivo é enviado diretamente para o Kinescope"
Client->>TusEndpoint: "PATCH chunk_1 (bytes do arquivo)"
TusEndpoint-->>Client: "204 No Content (Upload-Offset)"
Client->>TusEndpoint: "PATCH chunk_2 (bytes do arquivo)"
TusEndpoint-->>Client: "204 No Content (Upload-Offset)"
Note over Client,TusEndpoint: "Repete até o upload estar completo"
Client->>TusEndpoint: "PATCH ultimo_chunk (bytes do arquivo)"
TusEndpoint-->>Client: "204 No Content (Upload completo)"
O contrato do método /upload
O que o cliente envia → seu backend
Se você usa tus-js-client, uma opção conveniente é aceitar cabeçalhos Tus padrão:
Upload-Length: tamanho do arquivo em bytesUpload-Metadata: metadados (por exemplo,filenameem base64)
Para desenvolvedores: Nos exemplos abaixo, os metadados são obtidos do cabeçalho
Upload-Metadatae o tamanho deUpload-Length. Esta não é a única opção: esses valores também podem ser aceitos em JSON se for mais conveniente para seu frontend.
O que seu backend retorna → cliente
Existem duas opções funcionais:
- Opção A (como nos exemplos abaixo): Resposta
201 Created+ cabeçalhoLocation: <tus-endpoint>, ou redirecionamento para endpoint - Opção B:
200 OK+ JSON{ "endpoint": "<tus-endpoint>" }(e no frontend você usauploadURL)
Se você usar Opção A, certifique-se de que CORS permite ao cliente ler o cabeçalho Location (Access-Control-Expose-Headers: Location é necessário).
Exemplo de solicitação para /v2/init do Kinescope
Seu backend deve enviar uma solicitação de inicialização de upload:
curl -X POST 'https://uploader.kinescope.io/v2/init' \
-H 'Authorization: Bearer <KINESCOPE_API_TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"parent_id": "<PROJECT_OR_FOLDER_ID>",
"type": "video",
"filename": "example.mp4",
"title": "example.mp4",
"filesize": 123456789
}'
Em resposta, o Kinescope retornará 201 Created e um objeto contendo data.endpoint — este é o endpoint Tus para upload.
Exemplo de implementação de backend
Aqui está um exemplo de handler de backend em Go:
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
)
const (
kinescopeAPIToken = "11111111-1111-1111-1111-111111111111"
kinescopeUploadInitURL = "https://uploader.kinescope.io/v2/init"
)
type KinescopeInitResponse struct {
Data struct {
ID string `json:"id"`
Endpoint string `json:"endpoint"`
} `json:"data"`
}
// Handler para solicitação de inicialização de upload
func handleUploadInit(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// Definir cabeçalhos CORS
if origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers",
"Origin, Content-Type, Tus-Resumable, Upload-Length, Upload-Metadata")
w.Header().Set("Access-Control-Allow-Methods",
"POST, GET, HEAD, PATCH, DELETE, OPTIONS")
w.Header().Set("Access-Control-Expose-Headers", "Location")
}
// Manipular solicitação OPTIONS
if r.Method == "OPTIONS" {
w.Header().Set("Access-Control-Max-Age", "86400")
w.WriteHeader(http.StatusOK)
return
}
// Analisar metadados do cabeçalho Upload-Metadata
metadata := parseMetadataHeader(r.Header.Get("Upload-Metadata"))
// Analisar tamanho do arquivo do cabeçalho Upload-Length
filesize, err := strconv.ParseInt(r.Header.Get("Upload-Length"), 10, 64)
if err != nil || filesize <= 0 {
http.Error(w, "bad header Upload-Length", http.StatusBadRequest)
return
}
// Construir solicitação para a API do Kinescope
requestBody := map[string]interface{}{
"client_ip": r.RemoteAddr,
"parent_id": "seu ID de projeto ou pasta aqui",
"type": "video",
"title": metadata["filename"],
"filename": metadata["filename"],
"filesize": filesize,
}
// Chamar a API do Kinescope para inicializar upload
body, _ := json.Marshal(requestBody)
req, _ := http.NewRequest("POST", kinescopeUploadInitURL, strings.NewReader(string(body)))
req.Header.Set("Authorization", "Bearer "+kinescopeAPIToken)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil || resp.StatusCode != http.StatusCreated {
http.Error(w, fmt.Sprintf("kinescope api response status=%d", resp.StatusCode),
http.StatusBadRequest)
return
}
defer resp.Body.Close()
var result KinescopeInitResponse
json.NewDecoder(resp.Body).Decode(&result)
// Retornar redirecionamento para endpoint Tus
w.Header().Set("Location", result.Data.Endpoint)
w.WriteHeader(http.StatusCreated)
}
// Analisar cabeçalho Upload-Metadata
func parseMetadataHeader(header string) map[string]string {
meta := make(map[string]string)
if header == "" {
return meta
}
elements := strings.Split(header, ",")
for _, element := range elements {
parts := strings.Fields(strings.TrimSpace(element))
if len(parts) != 2 {
continue
}
decoded, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
continue
}
meta[parts[0]] = string(decoded)
}
return meta
}
Exemplo de implementação de frontend
Use a biblioteca tus-js-client para trabalhar com o protocolo Tus no navegador.
Exemplo HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Demo Upload Tus</title>
</head>
<body>
<input type="file" id="file-input">
</body>
<script src="https://cdn.jsdelivr.net/npm/tus-js-client@latest/dist/tus.js"></script>
<script src="upload.js"></script>
</html>
Exemplo de código JavaScript (upload.js):
function initializeUpload(file, backendEndpoint) {
const upload = new tus.Upload(file, {
// Opção 1: endpoint no seu backend (recomendado)
endpoint: backendEndpoint,
// Opção 2: uploadURL direto (se você já tiver um endpoint Tus)
// uploadURL: "https://uploader.kinescope.io/v2/upload/0966958f-638b-4aab-bf4a-7f9860a57a93",
retryDelays: [0, 3000, 5000, 10000, 20000],
chunkSize: 10000000, // 10 MB por parte
metadata: {
filename: file.name,
filetype: file.type
},
onError: function(error) {
console.error("Erro de upload:", error);
},
onProgress: function(bytesUploaded, bytesTotal) {
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
console.log(`Enviado: ${bytesUploaded} / ${bytesTotal} (${percentage}%)`);
},
onSuccess: function() {
console.log(`Arquivo ${upload.file.name} enviado com sucesso. URL: ${upload.url}`);
}
});
// Encontrar uploads anteriores para retomar
upload.findPreviousUploads()
.then(function(previousUploads) {
if (previousUploads.length > 0) {
upload.resumeFromPreviousUpload(previousUploads[0]);
}
upload.start();
})
.catch(function(error) {
console.error("Erro ao encontrar uploads anteriores:", error);
upload.start();
});
}
document.addEventListener('DOMContentLoaded', function() {
const fileInput = document.getElementById('file-input');
if (!fileInput) {
console.error('Elemento file-input não encontrado');
return;
}
fileInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (!file) {
return;
}
const backendEndpoint = 'https://seu-backend.com/upload';
initializeUpload(file, backendEndpoint);
});
});
Solução de problemas
Erro CORS e cabeçalho Location não visível
Problema: Erro CORS no navegador e o cabeçalho Location não está visível.
Solução: Verifique se seu backend define Access-Control-Expose-Headers: Location.
403/401 do Kinescope ao chamar /v2/init
Problema: O Kinescope retorna um erro de autorização.
Solução: Verifique o token e os direitos de acesso, certifique-se de que o token não está expirado ou revogado.
Upload não continua após uma queda
Problema: Após uma queda de conexão, o upload não retoma.
Solução: Habilite findPreviousUploads()/resumeFromPreviousUpload() e não altere endpoint/uploadURL para o mesmo arquivo.
Erros de rede frequentes
Problema: Erros de rede constantes durante o upload.
Solução: Reduza chunkSize e configure retryDelays.
O que enviar ao suporte
Se você precisar de ajuda do suporte técnico, anexe:
- URL da página/aplicação onde o upload ocorre
- Hora do erro e fuso horário
- Log de erros do console do navegador (veja Copiando erros do console do navegador )
- Arquivo HAR se o problema parecer relacionado à rede (veja Salvando a interação navegador-servidor em um arquivo HAR )
- Exemplo de solicitação
curldo seu backend paraPOST https://uploader.kinescope.io/v2/init(sem tokens ou dados pessoais)
Canal de suporte: o chat de suporte na interface do Kinescope.
Pronto! Agora você pode configurar o upload de arquivos grandes via protocolo Tus.
O que fazer a seguir?
- Upload de arquivos via API — outros métodos para fazer upload de vídeos
- Regras gerais da API — autorização e formato de solicitação
- Kinescope API — documentação completa da API
Ainda tem dúvidas? Escreva para o chat de suporte na interface do Kinescope — nossos especialistas vão ajudar!