Envoyer des fichiers à MinIO avec cURL et des URL PUT présignées
Pour exporter un fichier local vers MinIO, générez une URL PUT présignée pour un bucket et une clé
d’objet, puis envoyez le fichier avec curl --upload-file. Ce guide vous explique comment
mettre en place un serveur local, créer le bucket et télécharger l’objet téléversé pour vérifier
que ses octets correspondent à ceux de votre fichier.
Comprendre ce que l’URL autorise
MinIO expose une API HTTP compatible S3. Un SDK signe une requête avec votre clé d’accès et votre
clé secrète ; cURL envoie les octets en utilisant cette signature. Le SDK et le client en ligne de
commande mc sont des outils, pas des méthodes d’authentification distinctes.
Une URL présignée est un justificatif d’accès au porteur. Toute personne disposant de l’URL de cet exemple peut effectuer une requête PUT sur la clé d’objet exacte qu’elle désigne pendant 10 minutes, y compris pour remplacer son contenu par d’autres octets. Elle n’authentifie pas le contenu d’un fichier local donné. Ne la laissez pas apparaître dans l’historique du shell, les journaux ou les messages partagés. Un téléversement par formulaire avec une politique POST est une opération différente ; utilisez l’opération PUT présignée du SDK.
Préparer une démonstration locale
Le dépôt communautaire de MinIO a été archivé le 25 avril 2026 et indique qu’il n’est plus maintenu. La version compilée ci-dessous à partir d’un code source figé sert aux tests de compatibilité en local et ne constitue pas une recommandation pour un nouveau déploiement en production. Pour la production, utilisez un service maintenu et respectez ses exigences en matière d’authentification et de TLS.
Prérequis
Utilisez Linux avec Bash, cURL, Go 1.26.8, Node.js 24.15.0 et Corepack avec Yarn 4.12.0. Les exemples
utilisent la prise en charge native de TypeScript par Node ;
les fichiers .mts s’exécutent en tant que modules ES, même au sein d’un
projet parent CommonJS. La version figée du serveur est RELEASE.2025-10-15T17-29-55Z, et celle du
SDK JavaScript est minio@8.0.7. Pour compiler le serveur, il vous faut un accès
réseau, de l’espace disque pour les dépendances Go et quelques minutes de compilation.
Partez d’un répertoire accessible en écriture. Le bloc suivant crée un nouveau répertoire
minio-curl-demo et y installe son propre SDK. Il refuse d’utiliser un répertoire
existant. Son fichier de verrouillage en fait un projet Yarn distinct.
Ses paramètres de paquet et de cache limitent l’installation à ce répertoire. Le shell parent
reste dans son répertoire d’origine.
(
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
)
Configuration initiale
Compilez le code source figé du serveur
dans le répertoire bin de la démonstration. Exécutez ce bloc depuis le même
répertoire parent que le bloc précédent. Si la compilation échoue, arrêtez-vous ici ; ne démarrez
pas un ancien binaire issu d’une compilation précédente.
(
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
)
Ouvrez deux terminaux Bash dans ce répertoire parent. Collez ces paramètres dans les deux terminaux. Choisissez deux ports inutilisés si ceux-ci sont occupés, en conservant les mêmes valeurs dans les deux terminaux. Les justificatifs d’accès sont des valeurs de démonstration volontairement publiques ; ne les utilisez qu’avec ce serveur accessible uniquement via l’interface de bouclage.
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'
Dans le premier terminal, démarrez le serveur au premier plan. Laissez-le fonctionner pendant que
vous utilisez le second terminal. Appuyez sur Ctrl+C dans le premier terminal pour l’arrêter une
fois que vous avez terminé ; le shell continue de s’exécuter et les objets restent dans
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}"
)
Créer le bucket et le script de signature
Enregistrez chacun des fichiers TypeScript suivants dans minio-curl-demo.
Le client partagé vérifie que le point de terminaison est une origine, sans préfixe de chemin ni
justificatifs d’accès intégrés. Sa région correspond à celle de ce serveur local. Pour un
déploiement existant, utilisez sa région réelle et des justificatifs d’accès limités au bucket ;
les justificatifs d’accès root utilisés ici servent uniquement à mettre en place la démonstration
privée.
Enregistrez sous 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',
})
}
Enregistrez sous prepare-bucket.mts. Signer une URL ne crée pas de bucket ; c’est cette
étape qui le crée.
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
})
Dans le second terminal, créez le bucket. Une exécution réussie affiche
Bucket ready: curl-demo. Si le serveur n’est pas prêt, attendez son message de démarrage et
relancez cette commande.
(cd minio-curl-demo && node prepare-bucket.mts curl-demo)
Générer des URL présignées
Enregistrez sous presign.mts. Ce script signe une requête PUT ou GET et produit
une ligne de configuration cURL que les commandes de téléversement et de vérification transmettent
via l’entrée standard. Les deux opérations utilisent une durée de validité de 600 secondes.
La référence de l’API du SDK documente ces deux méthodes distinctes.
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
})
Ne lancez pas le script de signature seul dans un terminal dont la session est enregistrée : sa sortie contient l’URL présignée. Le SDK encode la clé d’objet. Transmettez la clé d’origine au script de signature et ne modifiez pas l’URL obtenue, y compris son hôte, son port, son chemin et sa chaîne de requête.
Téléverser un fichier et journaliser le résultat
Enregistrez sous upload.sh dans le répertoire de démonstration. Exécutez-le avec
Bash ; ne le chargez pas dans le shell courant. Il accepte un fichier local ordinaire, un bucket
et une clé d’objet explicite. Les fichiers vides sont valides. Préfixez les noms de fichiers tels
que - ou -report.bin par ./
pour qu’ils désignent des fichiers plutôt que l’entrée standard de 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 est la première option de cURL afin qu’il ignore le
.curlrc de l’appelant. --config - lit l’URL depuis l’entrée
standard, afin qu’elle ne figure pas dans les arguments du processus cURL.
--globoff traite les crochets et les accolades des noms de fichiers comme des
caractères littéraux. --fail fait échouer le transfert en cas de rejet HTTP
et pipefail propage les échecs de transfert ou de journalisation tout au long
de la chaîne de commandes. L’affectation vérifie séparément le résultat du script de signature.
Le script masque les diagnostics bruts du transfert et ne journalise que le statut HTTP, sans le
corps de la réponse ni l’URL. Consultez le
manuel de cURL pour la syntaxe de configuration et les options.
Exécutez ce bloc depuis le répertoire parent dans le second terminal. Il crée ou remplace le
fichier local de démonstration sample.txt et le téléverse sous une clé contenant
des espaces et un signe plus :
(
cd minio-curl-demo &&
printf 'hello, MinIO\n' > sample.txt &&
bash upload.sh sample.txt curl-demo 'exports/sample + 1.txt'
)
Le résultat attendu est HTTP 200 suivi de Uploaded sample.txt to curl-demo/exports/sample + 1.txt.
La gestion des versions est désactivée pour ce bucket : une requête PUT réussie sur une clé
existante remplace les octets précédemment stockés. Choisissez une nouvelle clé si vous
souhaitez conserver l’ancien objet. En cas d’URL expirée, de signature incorrecte ou de bucket
manquant, le téléversement doit échouer sans afficher le message Uploaded.
Télécharger et comparer les octets stockés
Un statut HTTP 200 prouve que le serveur a accepté la requête PUT. Vérifiez séparément l’objet
stocké avec une requête GET signée. Ce bloc écrit downloaded.txt, en remplaçant ce
fichier local si le téléchargement a lieu, puis le compare à sample.txt.
Si la comparaison réussit, Verified identical bytes s’affiche.
(
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
)
Diagnostiquer un échec de téléversement
Si la signature échoue, vérifiez les paramètres d’environnement et les trois arguments du script.
Un point de terminaison avec un préfixe de chemin de proxy, tel que https://storage.example.com/minio/,
est refusé. Utilisez l’origine de l’API S3 du service et HTTPS pour un point de terminaison distant.
HTTP 000 signifie qu’aucune réponse HTTP n’a été obtenue. Vérifiez que le
serveur s’exécute sur le port configuré, que le fichier est lisible et que le certificat est de
confiance. Ne désactivez pas la validation des certificats pour permettre à un téléversement distant
de réussir. HTTP 403 peut indiquer une signature invalide ou expirée, des
justificatifs d’accès incorrects, des autorisations refusées ou un décalage d’horloge.
HTTP 404 peut indiquer un bucket manquant ; le script de signature peut produire
une URL pour un bucket qui n’existe pas.
Un échec de journalisation peut se produire après que le serveur a stocké l’objet. Par exemple,
upload.log peut être un répertoire ou ne pas être accessible en écriture. Le
script signale un échec parce qu’il n’a pas pu enregistrer le statut ; vérifiez l’objet avec GET
avant de décider de réessayer. Un client déconnecté peut aussi laisser le résultat incertain.
Réutiliser la même clé peut remplacer un objet déjà accepté.
Choisir l’étape suivante
Ce script envoie un fichier en une seule requête PUT, avec un délai maximal de transfert de 120 secondes. Il ne gère ni les téléversements multiparties, ni les transferts avec reprise, ni la synchronisation de répertoires. Pour ces tâches, utilisez un SDK ou un client de stockage offrant le comportement requis et vérifiez sa politique de remplacement. Pour un flux de travail par lots propre à AWS, consultez le guide sur les exportations par lots vers Amazon S3 avec cURL et l’AWS CLI (English).
