Importer des fichiers depuis MinIO en Java
Téléchargez un objet MinIO dans un fichier temporaire, puis publiez-le à la destination demandée après avoir lu et fermé la réponse. Cette commande Java préserve une destination existante et se termine avec le code 1 si l’import échoue. Une démonstration locale téléverse d’abord des octets binaires connus pour vous permettre de vérifier ce que l’outil d’import a réellement enregistré.
Configurer votre environnement Java
Ce guide utilise le SDK Java MinIO avec un point de terminaison auto-hébergé et une clé d’objet précise. Pour les informations d’accès AWS, les régions et le comportement du gestionnaire de transferts, consultez le guide Java pour Amazon S3 (English).
Utilisez Linux, Bash, OpenJDK 21.0.12.1 et Maven 3.9.16. L’exemple utilise le
SDK Java MinIO 9.0.3. Maven télécharge le SDK et les plugins de
compilation depuis Maven Central. Définissez JAVA_HOME pour pointer vers votre
installation du JDK si Maven utilise une autre version de Java.
Pour la démonstration locale éphémère, prévoyez aussi cURL avec la prise en charge d’AWS Signature V4
et un binaire serveur minio disponible dans PATH.
La réexécution a utilisé cURL 8.22.0 et MinIO compilé à partir de la révision 9e49d5e7a648.
MinIO Community Edition est archivée et n’est plus maintenue.
Cet environnement de test est destiné à l’apprentissage local, et ne constitue pas une recommandation
pour un nouveau déploiement en production. Si vous gérez déjà un service MinIO, vous pouvez utiliser
l’outil d’import directement avec ce service.
Choisissez un répertoire parent accessible en écriture. Collez-y ce bloc ; il crée un nouveau projet
et laisse votre shell dans le répertoire parent. Si minio-import existe déjà, le
bloc s’arrête avant d’y écrire.
(
set -eu
mkdir minio-import
cd minio-import
mkdir -p .mvn src/main/java
printf '<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0"/>\n' > settings.xml
)
Enregistrez les fichiers suivants dans minio-import. Son propre répertoire
.mvn empêche Maven d’utiliser la configuration .mvn
d’un projet englobant. Le lanceur utilise des paramètres vides et un dépôt .m2
local au projet : il ne met donc pas à jour votre cache habituel de dépendances Maven.
Intégrer le SDK Java MinIO
Enregistrez ce fichier pom.xml complet. La version Java cible et les versions
des plugins sont explicites ; aucun POM parent englobant n’est requis.
OkHttp 5 exige que son artefact JVM soit spécifié explicitement dans Maven ;
okhttp-jvm correspond ici à la version utilisée par le SDK.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>example</groupId>
<artifactId>minio-import</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>9.0.3</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp-jvm</artifactId>
<version>5.3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.9.0</version>
</plugin>
</plugins>
</build>
</project>
Importer des fichiers depuis MinIO : guide pas à pas
Enregistrez ce code dans src/main/java/MinIOFileImporter.java. Les arguments sont le compartiment, la clé
d’objet exacte et la destination locale. Fournissez la clé telle qu’elle est stockée, par exemple
reports/April report.bin ; ne la transformez pas en URL et ne lui appliquez pas vous-même un
encodage en pourcentage.
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.MinioClient;
import io.minio.errors.ErrorResponseException;
import java.io.IOException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class MinIOFileImporter {
public static void main(String[] args) {
try {
long bytes = download(args);
System.out.println("Imported " + bytes + " bytes.");
} catch (FileAlreadyExistsException e) {
System.err.println("Destination exists; choose a new path.");
System.exit(1);
} catch (ErrorResponseException e) {
System.err.println("Storage refused the download (HTTP " + e.response().code() + ").");
System.exit(1);
} catch (Exception e) {
System.err.println("Import failed (" + e.getClass().getSimpleName() + "). Check configuration, network, and disk.");
System.exit(1);
}
}
private static String required(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) {
throw new IllegalArgumentException("Missing " + name);
}
return value;
}
private static long download(String[] args) throws Exception {
if (args.length != 3 || args[0].isBlank() || args[1].isEmpty() || args[2].isBlank()) {
throw new IllegalArgumentException("Expected bucket, object key, and destination");
}
String endpoint = required("MINIO_ENDPOINT");
String region = required("MINIO_REGION");
String accessKey = required("MINIO_ACCESS_KEY");
String secretKey = required("MINIO_SECRET_KEY");
Path destination = Path.of(args[2]).toAbsolutePath();
if (Files.exists(destination, LinkOption.NOFOLLOW_LINKS)) {
throw new FileAlreadyExistsException(destination.toString());
}
if (!Files.isDirectory(destination.getParent())) {
throw new IOException("Destination parent must already exist");
}
Path staged = Files.createTempFile(destination.getParent(), ".minio-", ".part");
try {
long copied;
try (MinioClient client = MinioClient.builder()
.endpoint(endpoint).region(region).credentials(accessKey, secretKey).build()) {
client.setTimeout(10_000, 30_000, 30_000);
try (GetObjectResponse response = client.getObject(
GetObjectArgs.builder().bucket(args[0]).object(args[1]).build())) {
String length = response.headers().get("Content-Length");
if (length == null) throw new IOException("Missing Content-Length");
long expected = Long.parseLong(length);
copied = Files.copy(response, staged, StandardCopyOption.REPLACE_EXISTING);
if (expected < 0 || copied != expected) {
throw new IOException("Incomplete response body");
}
}
}
Files.move(staged, destination);
return copied;
} finally {
Files.deleteIfExists(staged);
}
}
}
Le contrat de getObject du SDK
impose de fermer la réponse renvoyée. Ici, la réponse et le client sont tous deux fermés avant la
publication. Un objet vide est valide et produit un fichier de zéro octet. La comparaison des
longueurs détecte un corps incomplet ; elle ne constitue ni une somme de contrôle indépendante ni
la preuve que le serveur a stocké le contenu attendu.
Enregistrez download.sh à la racine du projet. Exécutez-le toujours depuis ce
répertoire. Il compile la classe fournie et copie les dépendances d’exécution avant de lancer Java.
Le shell s’arrête si la compilation échoue, même si une ancienne version compilée de la classe est
encore présente.
#!/usr/bin/env bash
set -eu
if [ ! -d .mvn ] || [ ! -f settings.xml ] || [ ! -f src/main/java/MinIOFileImporter.java ]; then
printf 'Run download.sh from the minio-import project root.\n' >&2
exit 1
fi
if [ "$#" -ne 3 ]; then
printf 'Usage: bash download.sh BUCKET OBJECT_KEY DESTINATION\n' >&2
exit 1
fi
MAVEN_SKIP_RC=1 MAVEN_ARGS= MAVEN_OPTS= MAVEN_BASEDIR="$PWD" \
mvn --batch-mode --no-transfer-progress --settings settings.xml \
--global-settings settings.xml -Dmaven.repo.local="$PWD/.m2" \
compile dependency:copy-dependencies -DincludeScope=runtime
java -cp 'target/classes:target/dependency/*' MinIOFileImporter "$@"
Utilisez un répertoire local que vous contrôlez et exécutez un seul outil d’import par destination.
Le déplacement n’utilise pas REPLACE_EXISTING, conformément à la
politique de déplacement de Java. Cette procédure séquentielle ne
garantit ni la publication concurrente ni la durabilité en cas de plantage. Les échecs de transfert
pris en charge entraînent la suppression des fichiers intermédiaires et laissent la destination
absente. L’arrêt forcé de la JVM peut laisser un fichier .minio-*.part ; ne le
supprimez qu’après avoir confirmé l’arrêt de l’outil d’import. Une défaillance du système de fichiers
lors de la publication ou du nettoyage peut nécessiter une inspection du répertoire avant de réessayer.
Vérifier un objet local, du téléversement au téléchargement
Enregistrez demo.sh à la racine du projet. Il démarre un serveur privé sur
l’interface de bouclage avec des informations d’accès jetables, téléverse via cURL un fichier binaire
de test de huit octets, puis appelle download.sh. Il conserve le fichier de test,
le journal du serveur, les données du serveur et le fichier téléchargé pour permettre leur inspection,
et arrête son serveur à la sortie. Il ne contacte aucun point de terminaison public de démonstration.
#!/usr/bin/env bash
set -eu
port=${1:-19000}
case "$port" in ''|*[!0-9]*) printf 'Supply a numeric local port.\n' >&2; exit 1;; esac
if [ "$port" -lt 1024 ] || [ "$port" -gt 65535 ]; then
printf 'Use a local port from 1024 through 65535.\n' >&2
exit 1
fi
if [ ! -f download.sh ] || [ ! -d .mvn ]; then
printf 'Run demo.sh from the minio-import project root.\n' >&2
exit 1
fi
command -v minio >/dev/null
command -v curl >/dev/null
mkdir demo-data
mkdir demo-certs
export MINIO_ENDPOINT="http://127.0.0.1:$port" MINIO_REGION=us-east-1
export MINIO_ACCESS_KEY=local-demo MINIO_SECRET_KEY=local-demo-secret
MINIO_ROOT_USER="$MINIO_ACCESS_KEY" MINIO_ROOT_PASSWORD="$MINIO_SECRET_KEY" \
MINIO_BROWSER=off minio server demo-data --certs-dir demo-certs \
--address "127.0.0.1:$port" \
> demo-server.log 2>&1 &
server_pid=$!
stop_server() {
kill "$server_pid" 2>/dev/null || :
wait "$server_pid" 2>/dev/null || :
}
trap stop_server EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
ready=0
for attempt in {1..100}; do
if ! kill -0 "$server_pid" 2>/dev/null; then
printf 'MinIO startup failed; inspect demo-server.log.\n' >&2
exit 1
fi
if grep -q '^API:' demo-server.log && \
curl -fsS --max-time 1 "$MINIO_ENDPOINT/minio/health/ready" >/dev/null 2>&1; then
ready=1
break
fi
sleep 0.1
done
if [ "$ready" -ne 1 ]; then
printf 'MinIO did not become ready; inspect demo-server.log.\n' >&2
exit 1
fi
printf '\000\377MinIO\n' > sample.bin
curl -fsS --max-time 10 --aws-sigv4 'aws:amz:us-east-1:s3' \
--user "$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY" -X PUT "$MINIO_ENDPOINT/demo-bucket"
curl -fsS --max-time 10 --aws-sigv4 'aws:amz:us-east-1:s3' \
--user "$MINIO_ACCESS_KEY:$MINIO_SECRET_KEY" --upload-file sample.bin \
"$MINIO_ENDPOINT/demo-bucket/fixtures/sample.bin"
bash download.sh demo-bucket 'fixtures/sample.bin' ./downloaded.bin
cmp sample.bin downloaded.bin
printf 'Verified downloaded.bin against sample.bin.\n'
L’option --aws-sigv4 de cURL signe les requêtes de
création du compartiment et de téléversement du fichier de test indépendamment de l’outil de
téléchargement Java. Choisissez un port local inutilisé et collez ce bloc depuis le répertoire parent :
(cd minio-import && bash demo.sh 19000)
Après les messages de compilation de Maven, attendez-vous aux lignes suivantes et au code de sortie 0 :
Imported 8 bytes.
Verified downloaded.bin against sample.bin.
Si vous relancez la démonstration complète, elle s’arrête à mkdir demo-data et renvoie
un échec, tout en préservant les fichiers précédents. Utilisez un nouveau répertoire de projet pour
une autre démonstration complète. L’outil d’import lui-même refuse également un fichier
downloaded.bin existant ; il ne supprime jamais ce fichier pour permettre à une
nouvelle tentative de réussir.
Utiliser votre point de terminaison MinIO existant
Fournissez MINIO_ENDPOINT, MINIO_REGION,
MINIO_ACCESS_KEY et MINIO_SECRET_KEY via l’environnement de votre
processus, avec des informations d’accès dotées de l’autorisation s3:GetObject
pour l’objet sélectionné. Utilisez le point de terminaison de l’API S3, pas l’adresse de la console
web. Veillez à ce que la région corresponde à celle de votre serveur. HTTP et les informations
d’accès du compte racine ci-dessus sont réservés à l’environnement de test sur l’interface de
bouclage ; utilisez HTTPS et le mécanisme de gestion des informations d’accès de votre organisation
pour votre service géré. Ne désactivez pas la vérification des certificats pour contourner une
erreur de magasin de certificats de confiance.
Une fois l’environnement configuré, collez ce bloc depuis la racine du projet, en remplaçant le compartiment et la clé par ceux d’un objet qui existe déjà :
bash download.sh my-bucket 'reports/April report.bin' './April report.bin'
Gérer les exceptions courantes
Le code 0 signifie que la réponse a été lue en entier, que les ressources ont été fermées et que la destination a été publiée. Le code 1 signifie que la commande a échoué ; le diagnostic omet volontairement les informations d’accès, les corps des réponses et les traces de pile. Un code HTTP 404 peut indiquer l’absence du compartiment ou de la clé exacte. Un code HTTP 403 peut indiquer des informations d’accès non valides ou un objet dont l’accès est refusé. Les erreurs de chemin local et les transferts incomplets entraînent également un échec.
Le délai d’expiration de connexion est de 10 secondes et ceux de lecture et d’écriture sont de
30 secondes, conformément aux
paramètres de délai d’expiration du SDK, en millisecondes.
Ce sont des délais d’expiration par opération, pas une échéance globale pour tout le téléchargement.
Il n’y a pas de boucle de nouvelle tentative au niveau de l’application : après un échec transitoire,
relancez toute la commande avec une destination absente. Chaque tentative crée un nouveau fichier
intermédiaire. Ajouter une nouvelle tentative autour de getObject uniquement
ne couvrirait pas les échecs survenant lors de la lecture du corps de sa réponse.
Dépannage
Connexion refusée
Vérifiez l’hôte et le port de l’API, assurez-vous que le serveur est en cours d’exécution et que
votre machine peut le joindre. Pour la démonstration locale, examinez demo-server.log ;
si le port est occupé, vous devez en choisir un autre avant de démarrer un nouvel environnement de
test. Une compilation qui échoue ne lance aucun téléchargement : résolvez donc d’abord le problème
indiqué par le diagnostic de Maven.
Accès refusé
Vérifiez l’autorisation d’accès du compte à l’objet, la région, ainsi que la casse et la ponctuation exactes de la clé. L’outil d’import ne liste pas les objets d’un préfixe et ne choisit pas le premier objet correspondant. Si une destination existe déjà, choisissez un nouveau nom de fichier local plutôt que de supprimer un résultat antérieur non vérifié.
Pour un import qui alimente un pipeline de traitement hébergé, le Robot 🤖 /minio/import (English) est une option d’intégration distincte. La commande locale ci-dessus produit un fichier pour votre propre flux de travail Java.
