Principais pontos
- Crie uma única instância do Uppy para cada componente de upload montado e destrua-a quando o componente proprietário for desmontado.
- Use o estado e os eventos do Uppy como estado externo em vez de copiar progresso, arquivos e erros para um estado React concorrente.
- Trate as restrições no cliente como feedback imediato e aplique a mesma política novamente em um Template ou receptor confiável.
Um upload de arquivos em React é um pequeno sistema com estado, não apenas um input e uma requisição POST. O componente de upload precisa sobreviver a novas renderizações, liberar recursos ao ser desmontado, explicar as restrições antes da transferência, se recuperar de falhas comuns de rede e distinguir bytes enviados de processamento de mídia concluído. O Uppy fornece essa máquina de estados de upload enquanto o React renderiza o estado atual.
O que mais importa
- Busque opções de Assembly assinadas e de curta duração em um endpoint de servidor autenticado; nunca exponha um Auth Secret no código React.
- Use novas tentativas limitadas para falhas transitórias, torne o cancelamento explícito e adicione recuperação persistida apenas quando a recuperação após recarregamento for um requisito real.
- Armazene o Assembly ID e reconcilie o processamento de forma independente quando o componente de upload puder desaparecer antes de o fluxo de trabalho terminar.
Defina o que significa “concluído” antes de escrever o componente
Um navegador pode terminar de enviar os bytes enquanto a mídia resultante ainda está sendo inspecionada, transformada e exportada. Decida qual estado a interface chama de concluído: seleção de arquivos, transferência, criação da Assembly, processamento, armazenamento durável ou publicação na aplicação. Os três níveis abaixo agrupam seleção de arquivos e transferência em sucesso da transferência, criação da Assembly e processamento em sucesso do processamento, e armazenamento durável e publicação em sucesso da aplicação. Um componente React pode exibir vários desses estados, mas não deve reduzi-los a um único indicador de sucesso.
Em um fluxo de trabalho da Transloadit, o Uppy é responsável pela fila e pelo estado da transferência no navegador. O plugin Transloadit cria uma Assembly e associa cada arquivo local a esse fluxo de trabalho. Sua aplicação deve guardar o Assembly ID assim que ele existir e, em seguida, reconciliar o resultado final fora do componente quando o processamento puder durar mais que a página. Uma barra de progresso completa não é um registro durável do ativo.
Sucesso da transferência
O receptor aceitou os bytes do arquivo. Esse é o estado representado pelo progresso do upload quando o plugin não espera pela codificação.
Sucesso do processamento
A Assembly atingiu um estado final de sucesso e produziu os Steps de resultado esperados.
Sucesso da aplicação
A aplicação armazenou os identificadores da Assembly e do ativo, confirmou a propriedade e disponibilizou o resultado de acordo com as regras do produto.
Crie uma única instância configurada do Uppy para cada componente de upload montado
Instale o Core, o Dashboard, o pacote de interface React, o plugin Transloadit mantido e o validador de schemas Zod para a resposta de autorização. Importe cada folha de estilo do Uppy uma única vez a partir de um ponto de entrada estável para que a ordem de carregamento continue previsível. O exemplo de componente abaixo importa os estilos dos pacotes diretamente; mova essas importações para o ponto de entrada da aplicação se é ali que seu framework ou bundler gerencia os estilos globais.
Construa o Uppy uma única vez durante o ciclo de vida do formulário montado. Um inicializador lazy de useState na próxima seção dá a cada componente de upload montado sua própria instância sem recriá-la a cada nova renderização. Não crie um singleton compartilhado no nível do módulo, a menos que todas as superfícies sejam intencionalmente uma única fila: caso contrário, formulários independentes veriam e removeriam os arquivos uns dos outros.
A função assíncrona assemblyOptions solicita autorização a um endpoint confiável imediatamente antes do upload. O endpoint precisa autenticar e autorizar o usuário atual, aplicar controles contra abuso, escolher um Template restrito e retornar um payload assinado de curta duração. O navegador valida o formato da resposta, mas nunca recebe o Auth Secret.
yarn add @uppy/core @uppy/dashboard @uppy/react @uppy/transloadit zodimport Uppy from '@uppy/core'
import Transloadit from '@uppy/transloadit'
import { z } from 'zod'
const assemblyOptionsSchema = z.object({
params: z.string().min(1),
signature: z.string().regex(/^(sha1|sha256|sha384):[0-9a-f]+$/),
})
async function fetchAssemblyOptions(): Promise<z.infer<typeof assemblyOptionsSchema>> {
const response = await fetch('/api/transloadit-params', {
method: 'POST',
headers: { Accept: 'application/json' },
})
if (!response.ok) {
throw new Error('Could not authorize this upload')
}
const responseBody: unknown = await response.json().catch(() => null)
const parsedOptions = assemblyOptionsSchema.safeParse(responseBody)
if (!parsedOptions.success) {
throw new Error('Could not authorize this upload')
}
return parsedOptions.data
}
export function createImageUploader(): Uppy {
return new Uppy({
autoProceed: false,
restrictions: {
allowedFileTypes: ['image/jpeg', 'image/png', 'image/webp'],
maxFileSize: 50 * 1024 * 1024,
maxNumberOfFiles: 5,
},
}).use(Transloadit, {
assemblyOptions: fetchAssemblyOptions,
retryDelays: [0, 1_000, 3_000, 5_000],
waitForEncoding: false,
})
}Renderize o progresso, o cancelamento e o estado da Assembly a partir do Uppy
O Uppy é um store de estado externo. useUppyState inscreve o React em valores selecionados sem manter uma segunda fila no estado do componente, enquanto useUppyEvent expõe eventos que não são campos duráveis do store. O componente lê a contagem de arquivos, o progresso agregado, o estado do upload ativo, o estado de erro bruto e o evento de criação da Assembly. Ele mapeia o erro para uma mensagem estável voltada ao usuário em vez de exibir o valor bruto e não espelha arquivos individuais em um valor useState separado.
O Dashboard oferece seleção de arquivos, arrastar e soltar, pré-visualizações para arquivos locais compatíveis, status por arquivo e controles de upload. A live region separada fornece à aplicação ao redor um anúncio de status conciso, e o estado disabled nativo do botão indica se o cancelamento está disponível. Mantenha os rótulos do próprio Dashboard localizados quando o produto oferecer suporte a vários idiomas; o título, o status e os erros ao redor precisam do mesmo tratamento.
Destrua a instância do Uppy quando o componente proprietário for desmontado. A destruição cancela o trabalho atual, remove os plugins instalados e libera os listeners. Portanto, mudanças de rota não devem ser a cópia exclusiva de progresso importante: guarde o Assembly ID e qualquer registro de tarefa da aplicação antes de depender de trabalho que possa continuar em outro lugar.
import type { ReactNode } from 'react'
import Dashboard from '@uppy/react/dashboard'
import { useUppyEvent, useUppyState } from '@uppy/react'
import { useEffect, useState } from 'react'
import { createImageUploader } from './createImageUploader.ts'
import '@uppy/core/css/style.min.css'
import '@uppy/dashboard/css/style.min.css'
export function ReactFileUploader(): ReactNode {
const [uppy] = useState(createImageUploader)
const error = useUppyState(uppy, (state) => state.error)
const fileCount = useUppyState(uppy, (state) => Object.keys(state.files).length)
const isUploading = useUppyState(
uppy,
(state) => Object.keys(state.currentUploads).length > 0,
)
const progress = useUppyState(uppy, (state) => state.totalProgress)
const [assemblyCreatedArgs, clearAssemblyCreated] = useUppyEvent(
uppy,
'transloadit:assembly-created',
)
useUppyEvent(uppy, 'cancel-all', clearAssemblyCreated)
const [assembly] = assemblyCreatedArgs
const assemblyId = assembly?.assembly_id
useEffect(() => {
return () => uppy.destroy()
}, [uppy])
let status = 'Choose up to five JPEG, PNG, or WebP images.'
if (fileCount > 0) status = 'Ready to upload.'
if (isUploading) status = `Upload ${progress}% complete.`
if (!isUploading && progress === 100) status = 'Files transferred. Processing may continue.'
if (error != null) status = 'Upload failed. Check the selected files and try again.'
return (
<section aria-labelledby="file-upload-heading">
<h2 id="file-upload-heading">Upload images</h2>
<Dashboard height={420} uppy={uppy} />
<p aria-live="polite" role="status">
{status}
</p>
{assemblyId != null ? (
<p>
Processing reference: <code>{assemblyId}</code>
</p>
) : null}
<button disabled={fileCount === 0} onClick={() => uppy.cancelAll()} type="button">
Cancel and remove files
</button>
</section>
)
}Use prévias e restrições como recursos de interface, não como limites de confiança
Uma prévia local ajuda a pessoa a perceber uma seleção errada antes de arcar com o custo do upload. Ela não é o resultado processado e não prova que o arquivo é seguro, decodificável, está com a orientação correta ou foi rotulado honestamente. Mantenha o trabalho de prévia limitado, porque decodificar muitas imagens grandes consome memória do navegador. Para formatos que o navegador não consegue pré-visualizar, mostre o nome do arquivo, o tipo declarado e o tamanho sem inventar uma miniatura.
As restrições do Uppy rejeitam erros óbvios logo no início: tipos permitidos, tamanho individual, tamanho total e quantidade de arquivos. Repita os mesmos limites em um receptor confiável ou no Template salvo, porque quem faz a chamada pode contornar o React e modificar os metadados do arquivo. Use o conteúdo detectado e uma tentativa de processamento quando for apropriado e, depois, armazene apenas os resultados aceitos. Mantenha allow_steps_override desativado quando o navegador não puder substituir o fluxo de trabalho aprovado. Quando uma prévia ou restrição rejeitar uma seleção, mostre uma mensagem estável para o usuário e mantenha fora da página as respostas brutas do provedor, os rastreamentos de pilha, as credenciais e os diagnósticos de armazenamento.
Feedback rápido
Explique os formatos, a quantidade e o tamanho aceitos antes da seleção e, depois, deixe o Uppy rejeitar violações conhecidas ao lado do controle.
Política autoritativa
Aplique autorização, limites de bytes, regras de conteúdo detectado, limites de processamento e destinos de exportação a partir do ponto em que o código do navegador deixa de ser confiável.
Falhas seguras
Capture falhas de autorização e de upload no limite confiável, mapeie-as para uma única mensagem estável para o usuário e encaminhe diagnósticos brutos apenas para logs do lado do servidor.
Projete a capacidade de retomada em torno da interrupção que você precisa superar
O plugin da Transloadit faz upload de arquivos locais via tus, que pode continuar uma transferência com falha a partir de um offset confirmado pelo servidor enquanto o recurso de upload continuar válido. retryDelays lida com um conjunto limitado de falhas transitórias durante o ciclo de vida atual do Uppy. O cancelamento é diferente: cancelAll() interrompe intencionalmente o trabalho atual, remove os arquivos e redefine o estado do upload; por isso, a interface deve informar que a seleção será removida.
Recarregar a página destrói o estado do React e do Uppy mantido em memória. A recuperação após o recarregamento exige estado persistido no cliente e recursos de upload compatíveis no servidor, como um fluxo de trabalho do plugin Golden Retriever configurado deliberadamente. Teste esse comportamento com a integração real da Transloadit antes de prometê-lo. Metadados locais persistidos podem ficar desatualizados, tornar-se sensíveis ou ficar inconsistentes com uma autorização expirada; por isso, defina a retenção e uma forma de descartar entradas irrecuperáveis.
A capacidade de retomada também tem um prazo operacional. Uma Assembly não pode continuar aceitando uploads para sempre, e parâmetros assinados de curta duração podem expirar antes que uma nova tentativa atrasada comece. Diferencie nova tentativa automática, pausa e retomada, restauração após recarregamento e início de uma nova Assembly; esses mecanismos resolvem falhas diferentes e podem reutilizar identificadores diferentes.
Entregue os arquivos transferidos a um fluxo de trabalho de mídia assíncrono
Um Template salvo deve descrever o grafo de processamento permitido: /upload/handle recebe os arquivos do navegador, /file/filter pode rejeitar entradas observadas sem suporte, Robots de transformação produzem derivados limitados e Robots de armazenamento exportam os resultados aprovados quando o armazenamento durável faz parte do fluxo de trabalho. A requisição assinada seleciona esse Template; o React não constrói Steps arbitrários nem carrega credenciais de armazenamento permanentes.
Escolha waitForEncoding com base no contrato da interface. Defini-lo como false permite que o navegador termine após a transferência e é apropriado quando a aplicação registra o Assembly ID, mostra um estado de processamento separado e obtém o resultado final a partir de uma Assembly Notification verificada ou de uma consulta posterior ao Assembly Status. Aguardar a codificação pode manter a interface alinhada a processamentos curtos, mas não substitui uma reconciliação durável caso a aba seja fechada.
Armazene o contexto da aplicação junto ao Assembly ID: o usuário ou tenant autenticado, o slot de ativo previsto, o Template ID, o horário de criação e a operação da aplicação que deu origem à Assembly. Quando uma notificação chegar, verifique a assinatura dela, trate entregas duplicadas de forma idempotente, confirme que a Assembly pertence ao registro esperado e salve apenas os campos de resultado de que o produto precisa.
Teste o ciclo de vida e o comportamento em falhas, não apenas o cenário em que tudo dá certo
Teste o componente com um arquivo pequeno permitido, um arquivo grande demais, uma extensão enganosa, um tipo sem suporte, vários arquivos no limite de quantidade, uma entrada de zero bytes, uma conexão lenta, um intervalo offline, uma rejeição pelo servidor, a expiração da autorização, o cancelamento pelo usuário, a desmontagem do componente e o recarregamento da página. Confirme qual estado é mantido, qual trabalho é interrompido e qual mensagem um usuário de teclado ou de leitor de tela recebe.
Teste o caminho real de assinatura e processamento, além do comportamento isolado do React. Um mock pode provar que o botão é desativado ou que um status muda, mas só um conjunto de dados de teste integrado ao fluxo real prova que os parâmetros assinados correspondem, que o tus retoma a partir do offset esperado, que o Template rejeita conteúdo inválido, que o Assembly ID é registrado e que a conclusão é reconciliada uma única vez. Mantenha pequenos os arquivos de teste e remova os dados temporários de produção após a execução.
Ciclo de vida do React
Renderize novamente sem substituir a instância do Uppy e, depois, desmonte o componente e verifique se os plugins e o trabalho ativo do navegador são limpos.
Interação acessível
Selecione arquivos sem arrastar e soltar, opere todos os controles pelo teclado e verifique se a rejeição, o progresso, o cancelamento e a conclusão são anunciados em texto.
Reconciliação do processamento
Feche a página após a criação da Assembly, entregue uma notificação de conclusão repetida e prove que um único ativo da aplicação chega ao estado final correto.
Detalhes técnicos que vale a pena conhecer
- O Uppy é um store externo com estado. Construí-lo no corpo da renderização cria uma instância nova e descartada a cada renderização e perde os arquivos na fila e o estado do upload. Manter uma única instância, mas executar novamente a configuração dela, pode, em vez disso, registrar plugins e listeners duplicados.
- O hook
useUppyStatese inscreve no store do Uppy por meio do contrato de store externo do React e seleciona apenas o estado de que um componente precisa. - O Dashboard do React instala seu plugin de interface ao ser montado e remove esse plugin de interface ao ser desmontado; a aplicação continua responsável por destruir a instância do Uppy que criou.
- As restrições do Uppy rejeitam seleções não permitidas no navegador, mas quem faz a chamada pode contornar o código do navegador e os tipos MIME declarados podem estar errados, então uma validação confiável continua sendo necessária.
- O plugin da Transloadit cria uma Assembly e faz o upload dos arquivos locais para o endpoint tus dela. O callback assíncrono
assemblyOptionsdo plugin pode obter parâmetros assinados imediatamente antes de um upload começar. - A opção
retryDelaystenta novamente falhas transitórias de upload tus enquanto a instância do Uppy e o estado do upload permanecem disponíveis; ela não restaura, por si só, uma transferência após um recarregamento da página ou o fechamento da aba. - Chamar
cancelAll()emite o cancelamento, interrompe o trabalho ativo por meio dos plugins de upload instalados, remove os arquivos atuais e redefine o estado de upload do Uppy. - Com
waitForEncodingdefinido como false, o upload do Uppy pode ser concluído após a transferência dos arquivos enquanto a Assembly continua o processamento. Persista o Assembly ID e use notificações verificadas ou uma consulta ao Assembly Status para obter uma conclusão durável.
Uma abordagem prática
- 1
Defina os arquivos aceitos, o destino dos bytes, o Template de processamento e o estado final da aplicação antes de construir o componente.
- 2
Crie uma única instância configurada do Uppy, renderize o Dashboard e um status acessível a partir do store dela e faça a limpeza na desmontagem.
- 3
Emita opções de Assembly assinadas e restritas no servidor e repita no Template os limites de arquivo e as verificações do conteúdo observado.
- 4
Teste rejeição, interrupção, cancelamento, nova tentativa, desmontagem, recarregamento, autorização expirada e conclusão assíncrona.
Quando a Transloadit é útil
Use o React Dashboard mantido pelo Uppy e o plugin Transloadit quando uma aplicação React precisar de uma interface de upload refinada, transferência tus retomável e Steps gerenciados de validação e transformação no lado do servidor em um Template salvo. Obtenha opções de Assembly assinadas e de curta duração em um servidor confiável e guarde cada Assembly ID quando o processamento puder durar mais do que a página.
Limite da arquitetura
O React é responsável pelo ciclo de vida do componente e renderiza o estado do upload. O Uppy é responsável pela seleção de arquivos no navegador, pelas pré-visualizações, pelas restrições, pelo estado da transferência e pelos controles de nova tentativa ou cancelamento. A Transloadit autoriza e executa o fluxo de trabalho de mídia salvo. A aplicação continua responsável pela permissão do usuário, pelos registros duráveis de ativos, pela política de publicação e pela reconciliação depois que o componente é desmontado.
Perguntas frequentes
O Uppy deve ser criado dentro de um componente React?
Sim, quando esse componente é responsável pela fila, mas crie a instância com um inicializador de estado preguiçoso (lazy) para que as novas renderizações reutilizem uma única instância. Destrua a instância quando o componente for desmontado. Use um provider compartilhado apenas quando vários componentes operarem intencionalmente sobre o mesmo uploader.
O plugin Transloadit precisa de um plugin Tus do Uppy separado?
Não, para arquivos locais enviados à Transloadit. O plugin Transloadit configura uploads tus para o endpoint da Assembly. Use o plugin Tus independente quando o destino for um servidor tus separado, e não uma Assembly da Transloadit.
As restrições de arquivos do Uppy são seguras?
Não. Elas melhoram o feedback no navegador, mas as requisições podem contornar o React e os metadados do arquivo podem ser falsos. Repita a autorização, os limites de bytes, a validação do conteúdo observado, as restrições do fluxo de trabalho e a política de armazenamento em fronteiras confiáveis.
A pré-visualização de imagem do Uppy redimensiona o arquivo enviado?
Não. Uma pré-visualização é estado da interface do navegador. Preserve o arquivo de origem selecionado e crie derivados reproduzíveis no fluxo de trabalho de processamento, a menos que o produto implemente deliberadamente uma etapa separada de pré-processamento no lado do cliente.
O retryDelays retoma um upload depois que a página é recarregada?
Não. Os intervalos entre novas tentativas cobrem falhas transitórias enquanto o estado atual do uploader continua disponível. A recuperação após recarregar a página exige metadados persistidos no cliente e um recurso de upload válido no servidor, e deve ser testada como um recurso separado.
Quando o waitForEncoding deve ser ativado?
Ative-o quando a interface montada precisar aguardar um processamento curto e exibir esses resultados diretamente. Deixe-o desativado quando for preferível que a transferência termine logo; nesse caso, guarde o Assembly ID e reconcilie o processamento por meio de notificações verificadas ou consultas de status.