Fazer upload de arquivos no MinIO: cURL e URLs PUT pré-assinadas
Para exportar um arquivo local para o MinIO, gere uma URL PUT pré-assinada para um bucket e uma
chave de objeto. Depois, envie o arquivo com curl --upload-file. Este guia compila um
servidor local, cria o bucket e baixa o objeto enviado para conferir se os bytes correspondem aos
do seu arquivo.
Entender o que a URL autoriza
O MinIO expõe uma API HTTP compatível com S3. Um SDK assina uma requisição com sua chave de acesso
e sua chave secreta; o cURL envia os bytes usando essa assinatura. O SDK e o cliente de linha de
comando mc são ferramentas, não métodos de autenticação distintos.
Uma URL pré-assinada é uma credencial ao portador. Qualquer pessoa com a URL deste exemplo pode executar PUT na chave exata do objeto por 10 minutos, inclusive substituindo seu conteúdo por bytes diferentes. Ela não autentica o conteúdo de um arquivo local específico. Mantenha-a fora do histórico do shell, de logs e de mensagens compartilhadas. Um upload por formulário com política POST é uma operação diferente; use a operação PUT pré-assinada do SDK.
Preparar uma demonstração local
O repositório comunitário do MinIO foi arquivado em 25 de abril de 2026 e informa que não recebe mais manutenção. A compilação abaixo, a partir de uma versão fixa do código-fonte, serve para testes locais de compatibilidade, não como recomendação para uma nova implantação em produção. Use um serviço com manutenção ativa em produção e siga seus requisitos de autenticação e TLS.
Pré-requisitos
Use Linux com Bash, cURL, Go 1.26.8, Node.js 24.15.0 e Corepack com Yarn 4.12.0. Os exemplos usam o
suporte nativo do Node a TypeScript;
arquivos .mts são executados como módulos ES mesmo dentro de um projeto
pai CommonJS. A versão fixada do servidor é RELEASE.2025-10-15T17-29-55Z, e a do SDK JavaScript é
minio@8.0.7. Compilar o servidor requer acesso à rede, espaço em disco para as
dependências do Go e alguns minutos de compilação.
Comece em um diretório com permissão de escrita. O bloco a seguir cria um novo diretório
minio-curl-demo e instala seu próprio SDK. Ele não aceita um diretório já existente.
Seu arquivo de lock o torna um projeto Yarn separado. Suas
configurações de pacotes e cache mantêm a instalação restrita a esse diretório. O shell pai
permanece no diretório original.
(
mkdir minio-curl-demo &&
cd minio-curl-demo &&
printf '%s\n' '{"name":"minio-curl-demo","private":true,"packageManager":"yarn@4.12.0","dependencies":{"minio":"8.0.7"}}' > package.json &&
touch yarn.lock &&
printf 'nodeLinker: node-modules\nenableGlobalCache: false\n' > .yarnrc.yml &&
env COREPACK_HOME="$PWD/.corepack" YARN_IGNORE_PATH=1 \
YARN_GLOBAL_FOLDER="$PWD/.yarn/global" corepack yarn install
)
Configuração inicial
Compile o código-fonte do servidor na versão fixada
com saída no diretório bin da demonstração. Execute este bloco a partir
do mesmo diretório pai do bloco anterior. Se a compilação falhar, pare aqui; não inicie um binário
remanescente de uma compilação anterior.
(
cd minio-curl-demo &&
mkdir bin data go-path go-cache tmp &&
env GOENV=off GOWORK=off GOTOOLCHAIN=local \
GOPATH="$PWD/go-path" GOCACHE="$PWD/go-cache" GOTMPDIR="$PWD/tmp" \
GOBIN="$PWD/bin" go install github.com/minio/minio@RELEASE.2025-10-15T17-29-55Z
)
Abra dois terminais Bash nesse diretório pai. Cole estas configurações em ambos os terminais. Escolha duas portas livres se estas estiverem ocupadas, usando os mesmos valores nos dois terminais. As credenciais são valores de demonstração deliberadamente públicos; use-as apenas com este servidor acessível somente via loopback.
export MINIO_API_PORT=19000
export MINIO_CONSOLE_PORT=19001
export MINIO_ENDPOINT="http://127.0.0.1:${MINIO_API_PORT}"
export MINIO_ACCESS_KEY='curl-demo-admin'
export MINIO_SECRET_KEY='local-demo-only-password'
No primeiro terminal, inicie o servidor em primeiro plano. Deixe-o em execução enquanto usa o
segundo terminal. Pressione Ctrl+C no primeiro terminal para pará-lo quando terminar; o shell
continua em execução e os objetos permanecem em minio-curl-demo/data.
(
cd minio-curl-demo &&
MINIO_ROOT_USER="$MINIO_ACCESS_KEY" MINIO_ROOT_PASSWORD="$MINIO_SECRET_KEY" \
./bin/minio server ./data --address "127.0.0.1:${MINIO_API_PORT}" \
--console-address "127.0.0.1:${MINIO_CONSOLE_PORT}"
)
Criar o bucket e o assinador
Salve cada um dos arquivos TypeScript a seguir dentro de minio-curl-demo.
O cliente compartilhado verifica se o endpoint é uma origem, sem prefixo de caminho nem credenciais
embutidas. Sua região corresponde à deste servidor local. Para uma implantação existente, use a
região real dela e credenciais com escopo restrito ao bucket; as credenciais de root usadas aqui
servem apenas para provisionar a demonstração privada.
Salve como minio-client.mts:
import { Client } from 'minio'
export function createClient(): Client {
const { MINIO_ENDPOINT, MINIO_ACCESS_KEY, MINIO_SECRET_KEY } = process.env
if (!MINIO_ENDPOINT || !MINIO_ACCESS_KEY || !MINIO_SECRET_KEY) {
throw new Error('Set the endpoint and credentials')
}
const endpoint = new URL(MINIO_ENDPOINT)
if (!['http:', 'https:'].includes(endpoint.protocol) || endpoint.username ||
endpoint.password || endpoint.pathname !== '/' || endpoint.search || endpoint.hash) {
throw new Error('Use an HTTP or HTTPS origin without credentials or a path prefix')
}
return new Client({
endPoint: endpoint.hostname,
port: Number(endpoint.port || (endpoint.protocol === 'https:' ? 443 : 80)),
useSSL: endpoint.protocol === 'https:',
accessKey: MINIO_ACCESS_KEY,
secretKey: MINIO_SECRET_KEY,
region: 'us-east-1',
})
}
Salve como prepare-bucket.mts. Assinar uma URL não cria um bucket; esta etapa o cria.
import { createClient } from './minio-client.mts'
async function main(): Promise<void> {
const [bucket, ...extra] = process.argv.slice(2)
if (!bucket || extra.length) throw new Error('Usage: node prepare-bucket.mts BUCKET')
const client = createClient()
if (!(await client.bucketExists(bucket))) await client.makeBucket(bucket, 'us-east-1')
console.log(`Bucket ready: ${bucket}`)
}
main().catch(() => {
console.error('Bucket setup failed; check the server, credentials, and bucket name')
process.exitCode = 1
})
No segundo terminal, crie o bucket. Uma execução bem-sucedida exibe Bucket ready: curl-demo.
Se o servidor não estiver pronto, aguarde a mensagem de inicialização e execute este comando
novamente.
(cd minio-curl-demo && node prepare-bucket.mts curl-demo)
Gerar URLs pré-assinadas
Salve como presign.mts. Ele assina PUT ou GET e emite uma linha de configuração
do cURL, que os comandos de upload e verificação passam pela entrada padrão. As duas operações
usam um prazo de expiração de 600 segundos. A
referência da API do SDK
documenta esses dois métodos distintos.
import { createClient } from './minio-client.mts'
async function main(): Promise<void> {
const [method, bucket, key, ...extra] = process.argv.slice(2)
if (!bucket || !key || extra.length || !['put', 'get'].includes(method ?? '')) {
throw new Error('Usage: node presign.mts put|get BUCKET KEY')
}
const client = createClient()
const url = method === 'put'
? await client.presignedPutObject(bucket, key, 600)
: await client.presignedGetObject(bucket, key, 600)
console.log(`url = ${JSON.stringify(url)}`)
}
main().catch(() => {
console.error('Signing failed; check the method, bucket, key, endpoint, and credentials')
process.exitCode = 1
})
Não execute o assinador isoladamente em um terminal cuja sessão esteja sendo gravada: sua saída contém a URL pré-assinada. O SDK codifica a chave do objeto para a URL. Passe a chave original ao assinador e mantenha a URL resultante inalterada, incluindo host, porta, caminho e string de consulta.
Fazer upload de um arquivo e registrar o resultado
Salve como upload.sh dentro do diretório da demonstração. Execute-o com Bash;
não use source. Ele aceita um arquivo local regular, um bucket e uma chave de objeto explícita.
Arquivos vazios são válidos. Adicione aos nomes de arquivo como - ou
-report.bin o prefixo ./ para que identifiquem arquivos,
e não a entrada padrão do cURL.
#!/usr/bin/env bash
set -euo pipefail
if [ "$#" -ne 3 ] || [ ! -f "$1" ] || [ ! -r "$1" ] || [ "$1" = '-' ]; then
printf 'Usage: bash upload.sh FILE BUCKET KEY (readable regular file)\n' >&2
exit 1
fi
file=$1
bucket=$2
key=$3
configuration=$(node presign.mts put "$bucket" "$key") || exit 1
if printf '%s\n' "$configuration" |
curl -q --config - --globoff --path-as-is --fail --silent \
--connect-timeout 5 --max-time 120 --upload-file "$file" \
--output /dev/null --write-out 'HTTP %{http_code}\n' 2>/dev/null |
tee -a upload.log; then
printf 'Uploaded %s to %s/%s\n' "$file" "$bucket" "$key"
else
printf 'Upload or status logging failed for %s; verify the object before retrying\n' "$file" >&2
exit 1
fi
-q é a primeira opção do cURL para que ele ignore o
.curlrc de quem o executa. --config - lê a URL da entrada
padrão, mantendo-a fora dos argumentos do processo do cURL. --globoff mantém
colchetes e chaves literais nos nomes de arquivo. --fail faz uma rejeição
HTTP causar a falha da transferência, e pipefail preserva as falhas de
transferência ou de registro ao longo do pipeline. A atribuição verifica o assinador separadamente.
O script suprime os diagnósticos brutos de transferência e registra apenas o status HTTP, não o
corpo da resposta nem a URL. Consulte o
manual do cURL para conhecer a sintaxe de configuração e as opções.
Execute este bloco a partir do diretório pai no segundo terminal. Ele cria ou substitui o arquivo
local de demonstração sample.txt e faz upload dele usando uma chave que contém
espaços e um sinal de mais:
(
cd minio-curl-demo &&
printf 'hello, MinIO\n' > sample.txt &&
bash upload.sh sample.txt curl-demo 'exports/sample + 1.txt'
)
A saída esperada é HTTP 200 seguida de Uploaded sample.txt to curl-demo/exports/sample + 1.txt.
O bucket não tem versionamento: um PUT bem-sucedido em uma chave existente substitui seus bytes
anteriores. Escolha uma nova chave se quiser manter o objeto antigo. Se a URL estiver expirada,
a assinatura for inválida ou o bucket não existir, a operação deve falhar sem a mensagem
Uploaded.
Baixar e comparar os bytes armazenados
Um HTTP 200 comprova que o servidor aceitou o PUT. Confira o objeto armazenado separadamente com
um GET assinado. Este bloco grava downloaded.txt, substituindo esse arquivo local
se o download prosseguir, e depois o compara com sample.txt. Uma comparação
bem-sucedida exibe Verified identical bytes.
(
set -euo pipefail
cd minio-curl-demo || exit 1
configuration=$(node presign.mts get curl-demo 'exports/sample + 1.txt') || exit 1
if printf '%s\n' "$configuration" |
curl -q --config - --globoff --path-as-is --fail --silent \
--connect-timeout 5 --max-time 120 --output downloaded.txt 2>/dev/null; then
cmp sample.txt downloaded.txt && printf 'Verified identical bytes\n'
else
printf 'Download failed; check the object and signing configuration\n' >&2
exit 1
fi
)
Diagnosticar uma falha no upload
Se a assinatura falhar, confira as configurações do ambiente e todos os três argumentos do script.
Um endpoint com prefixo de caminho de proxy, como https://storage.example.com/minio/, é rejeitado.
Use a origem da API S3 do serviço e use HTTPS para um endpoint remoto.
HTTP 000 significa que nenhuma resposta HTTP foi obtida. Confira se o
servidor está em execução na porta configurada, se o arquivo pode ser lido e se o certificado é
confiável. Não desative a validação de certificados para fazer um upload remoto funcionar.
HTTP 403 pode indicar uma assinatura inválida ou expirada, credenciais
incorretas, permissões negadas ou uma diferença entre os relógios. HTTP 404
pode indicar um bucket inexistente; o assinador pode gerar uma URL para um bucket que não existe.
Uma falha de registro pode ocorrer depois de o servidor armazenar o objeto. Por exemplo,
upload.log pode ser um diretório ou não permitir escrita. O script informa uma
falha porque não conseguiu registrar o status; confira com GET antes de decidir tentar novamente.
Um cliente desconectado também pode deixar o resultado incerto. Reutilizar a mesma chave pode
substituir um objeto que já foi aceito.
Escolher o próximo passo
Este script envia um arquivo em um único PUT, com um prazo de 120 segundos para a transferência. Ele não implementa uploads multipartes, transferências retomáveis nem sincronização de diretórios. Para essas tarefas, use um SDK ou um cliente de armazenamento com o comportamento necessário e confira sua política de substituição. Para um fluxo de trabalho em lote específico da AWS, veja exportações em lote para o Amazon S3 com cURL e a AWS CLI (English).
